nodebb-plugin-ezoic-infinite 1.4.4 → 1.4.5

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 +55 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.4.4",
3
+ "version": "1.4.5",
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
@@ -40,6 +40,7 @@
40
40
  retryQueue: [],
41
41
  retryQueueSet: new Set(),
42
42
  retryQueueRunning: false,
43
+ badIds: new Set(),
43
44
 
44
45
  scheduled: false,
45
46
  timer: null,
@@ -168,6 +169,34 @@
168
169
  return { id: null, recycled: null };
169
170
  }
170
171
 
172
+
173
+ function resetPlaceholderInWrap(wrap, id) {
174
+ try {
175
+ if (!wrap) return;
176
+ const old = wrap.querySelector && wrap.querySelector(`#${PLACEHOLDER_PREFIX}${id}`);
177
+ if (old) old.remove();
178
+ // Remove any leftover slot markup inside wrapper
179
+ wrap.querySelectorAll && wrap.querySelectorAll('iframe, ins, [id^="ezslot_"]').forEach(n => n.remove());
180
+ const ph = document.createElement('div');
181
+ ph.id = `${PLACEHOLDER_PREFIX}${id}`;
182
+ wrap.appendChild(ph);
183
+ } catch (e) {}
184
+ }
185
+
186
+ function isAdjacentAd(target) {
187
+ if (!target || !target.nextElementSibling) return false;
188
+ const next = target.nextElementSibling;
189
+ if (next && next.classList && next.classList.contains(WRAP_CLASS)) return true;
190
+ return false;
191
+ }
192
+
193
+ function isPrevAd(target) {
194
+ if (!target || !target.previousElementSibling) return false;
195
+ const prev = target.previousElementSibling;
196
+ if (prev && prev.classList && prev.classList.contains(WRAP_CLASS)) return true;
197
+ return false;
198
+ }
199
+
171
200
  function buildWrap(id, kindClass, afterPos) {
172
201
  const wrap = document.createElement('div');
173
202
  wrap.className = `${WRAP_CLASS} ${kindClass}`;
@@ -256,6 +285,7 @@
256
285
 
257
286
  function enqueueRetry(id) {
258
287
  if (!id) return;
288
+ if (state.badIds && state.badIds.has(id)) return;
259
289
  if (state.retryQueueSet.has(id)) return;
260
290
  state.retryQueueSet.add(id);
261
291
  state.retryQueue.push(id);
@@ -270,11 +300,20 @@
270
300
  const id = state.retryQueue.shift();
271
301
  if (!id) {
272
302
  state.retryQueueRunning = false;
303
+ state.badIds = new Set();
273
304
  return;
274
305
  }
275
306
  state.retryQueueSet.delete(id);
307
+ // If this id was previously attempted and still empty, force a full reset before re-requesting.
308
+ const attempts = (state.retryById.get(id) || 0);
309
+ const phNow = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
310
+ const wrapNow = phNow && phNow.parentElement;
311
+ if (attempts > 0 && wrapNow && wrapNow.isConnected && !isPlaceholderFilled(id)) {
312
+ destroyPlaceholderIds([id]);
313
+ resetPlaceholderInWrap(wrapNow, id);
314
+ }
276
315
  callShowAdsWhenReady(id);
277
- setTimeout(step, 950);
316
+ setTimeout(step, 1100);
278
317
  };
279
318
 
280
319
  step();
@@ -297,7 +336,7 @@
297
336
  }
298
337
 
299
338
  const tries = (state.retryById.get(id) || 0);
300
- if (tries >= 8) continue;
339
+ if (tries >= 8) { state.badIds && state.badIds.add(id); continue; }
301
340
 
302
341
  const r = safeRect(wrap);
303
342
  if (r && (r.top > window.innerHeight + 1200 || r.bottom < -1200)) continue;
@@ -423,8 +462,7 @@
423
462
  if (!el || !el.isConnected) continue;
424
463
 
425
464
  // Prevent adjacent ads (DOM-based, robust against virtualization)
426
- const nextSibling = el.nextElementSibling;
427
- if (nextSibling && nextSibling.classList && nextSibling.classList.contains(WRAP_CLASS)) {
465
+ if (isAdjacentAd(el) || isPrevAd(el)) {
428
466
  continue;
429
467
  }
430
468
 
@@ -447,7 +485,8 @@
447
485
  destroyPlaceholderIds([id]);
448
486
  wrap = pick.recycled.wrap;
449
487
  if (!moveWrapAfter(wrap, el, kindClass, afterPos)) continue;
450
- setTimeout(() => { enqueueRetry(id); }, 250);
488
+ resetPlaceholderInWrap(wrap, id);
489
+ setTimeout(() => { enqueueRetry(id); }, 450);
451
490
  } else {
452
491
  usedSet.add(id);
453
492
  wrap = insertAfter(el, id, kindClass, afterPos);
@@ -455,6 +494,17 @@
455
494
  }
456
495
 
457
496
  liveArr.push({ id, wrap });
497
+ // If adjacency ended up happening (e.g. DOM shifts), rollback this placement.
498
+ if (wrap && (wrap.previousElementSibling && wrap.previousElementSibling.classList && wrap.previousElementSibling.classList.contains(WRAP_CLASS)) || (wrap.nextElementSibling && wrap.nextElementSibling.classList && wrap.nextElementSibling.classList.contains(WRAP_CLASS))) {
499
+ try { wrap.remove(); } catch (e) {}
500
+ // Put id back if it was newly consumed (not recycled)
501
+ if (!(pick.recycled && pick.recycled.wrap)) {
502
+ try { kindPool.unshift(id); } catch (e) {}
503
+ try { usedSet.delete(id); } catch (e) {}
504
+ }
505
+ inserted -= 0; // no-op
506
+ continue;
507
+ }
458
508
  if (!(pick.recycled && pick.recycled.wrap)) {
459
509
  callShowAdsWhenReady(id);
460
510
  }