nodebb-plugin-ezoic-infinite 1.5.23 → 1.5.24

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 +54 -47
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.5.23",
3
+ "version": "1.5.24",
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
@@ -26,13 +26,13 @@
26
26
  pageKey: null,
27
27
  cfg: null,
28
28
 
29
- poolTopics: [],
30
- poolPosts: [],
31
- poolCategories: [],
32
-
33
- usedTopics: new Set(),
34
- usedPosts: new Set(),
35
- usedCategories: new Set(),
29
+ // Full lists (never consumed) + cursors for round-robin reuse
30
+ allTopics: [],
31
+ allPosts: [],
32
+ allCategories: [],
33
+ curTopics: 0,
34
+ curPosts: 0,
35
+ curCategories: 0,
36
36
 
37
37
  // throttle per placeholder id
38
38
  lastShowById: new Map(),
@@ -46,7 +46,6 @@
46
46
  heroDoneForPage: false,
47
47
  };
48
48
 
49
- const sessionDefinedIds = new Set();
50
49
  const insertingIds = new Set();
51
50
 
52
51
  // ---------- small utils ----------
@@ -205,9 +204,9 @@
205
204
 
206
205
  function initPools(cfg) {
207
206
  if (!cfg) return;
208
- if (state.poolTopics.length === 0) state.poolTopics = parsePool(cfg.placeholderIds);
209
- if (state.poolPosts.length === 0) state.poolPosts = parsePool(cfg.messagePlaceholderIds);
210
- if (state.poolCategories.length === 0) state.poolCategories = parsePool(cfg.categoryPlaceholderIds);
207
+ if (state.allTopics.length === 0) state.allTopics = parsePool(cfg.placeholderIds);
208
+ if (state.allPosts.length === 0) state.allPosts = parsePool(cfg.messagePlaceholderIds);
209
+ if (state.allCategories.length === 0) state.allCategories = parsePool(cfg.categoryPlaceholderIds);
211
210
  }
212
211
 
213
212
  // ---------- insertion primitives ----------
@@ -257,8 +256,22 @@
257
256
  }
258
257
  }
259
258
 
260
- function pickId(pool) {
261
- return pool.length ? pool.shift() : null;
259
+ function pickIdFromAll(allIds, cursorKey) {
260
+ const n = allIds.length;
261
+ if (!n) return null;
262
+
263
+ // Try at most n ids to find one that's not already in the DOM
264
+ for (let tries = 0; tries < n; tries++) {
265
+ const idx = state[cursorKey] % n;
266
+ state[cursorKey] = (state[cursorKey] + 1) % n;
267
+
268
+ const id = allIds[idx];
269
+ const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
270
+ if (ph && ph.isConnected) continue;
271
+
272
+ return id;
273
+ }
274
+ return null;
262
275
  }
263
276
 
264
277
  function showAd(id) {
@@ -280,7 +293,6 @@
280
293
  // Fast path
281
294
  if (typeof ez.showAds === 'function') {
282
295
  ez.showAds(id);
283
- sessionDefinedIds.add(id);
284
296
  return;
285
297
  }
286
298
 
@@ -294,7 +306,6 @@
294
306
  const el = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
295
307
  if (!el || !el.isConnected) return;
296
308
  window.ezstandalone.showAds(id);
297
- sessionDefinedIds.add(id);
298
309
  } catch (e) {}
299
310
  });
300
311
  }
@@ -348,7 +359,7 @@
348
359
  return Array.from(new Set(out)).sort((a, b) => a - b);
349
360
  }
350
361
 
351
- function injectBetween(kindClass, items, interval, showFirst, pool, usedSet) {
362
+ function injectBetween(kindClass, items, interval, showFirst, allIds, cursorKey) {
352
363
  if (!items.length) return 0;
353
364
 
354
365
  const targets = computeTargets(items.length, interval, showFirst);
@@ -362,14 +373,10 @@
362
373
  if (isAdjacentAd(el)) continue;
363
374
  if (findWrap(kindClass, afterPos)) continue;
364
375
 
365
- const id = pickId(pool);
376
+ const id = pickIdFromAll(allIds, cursorKey);
366
377
  if (!id) break;
367
-
368
- usedSet.add(id);
369
378
  const wrap = insertAfter(el, id, kindClass, afterPos);
370
379
  if (!wrap) {
371
- usedSet.delete(id);
372
- pool.unshift(id);
373
380
  continue;
374
381
  }
375
382
 
@@ -389,30 +396,35 @@
389
396
 
390
397
  const kind = getKind();
391
398
  let items = [];
392
- let pool = null;
393
- let usedSet = null;
399
+ let allIds = [];
400
+ let cursorKey = '';
394
401
  let kindClass = '';
402
+ let showFirst = false;
395
403
 
396
404
  if (kind === 'topic' && normalizeBool(cfg.enableMessageAds)) {
397
405
  items = getPostContainers();
398
- pool = state.poolPosts;
399
- usedSet = state.usedPosts;
406
+ allIds = state.allPosts;
407
+ cursorKey = 'curPosts';
400
408
  kindClass = 'ezoic-ad-message';
409
+ showFirst = normalizeBool(cfg.showFirstMessageAd);
401
410
  } else if (kind === 'categoryTopics' && normalizeBool(cfg.enableBetweenAds)) {
402
411
  items = getTopicItems();
403
- pool = state.poolTopics;
404
- usedSet = state.usedTopics;
412
+ allIds = state.allTopics;
413
+ cursorKey = 'curTopics';
405
414
  kindClass = 'ezoic-ad-between';
415
+ showFirst = normalizeBool(cfg.showFirstTopicAd);
406
416
  } else if (kind === 'categories' && normalizeBool(cfg.enableCategoryAds)) {
407
417
  items = getCategoryItems();
408
- pool = state.poolCategories;
409
- usedSet = state.usedCategories;
418
+ allIds = state.allCategories;
419
+ cursorKey = 'curCategories';
410
420
  kindClass = 'ezoic-ad-categories';
421
+ showFirst = normalizeBool(cfg.showFirstCategoryAd);
411
422
  } else {
412
423
  return;
413
424
  }
414
425
 
415
426
  if (!items.length) return;
427
+ if (!showFirst) { state.heroDoneForPage = true; return; }
416
428
 
417
429
  // Insert after the very first item (above-the-fold)
418
430
  const afterPos = 1;
@@ -421,14 +433,11 @@
421
433
  if (isAdjacentAd(el)) return;
422
434
  if (findWrap(kindClass, afterPos)) { state.heroDoneForPage = true; return; }
423
435
 
424
- const id = pickId(pool);
436
+ const id = pickIdFromAll(allIds, cursorKey);
425
437
  if (!id) return;
426
438
 
427
- usedSet.add(id);
428
439
  const wrap = insertAfter(el, id, kindClass, afterPos);
429
440
  if (!wrap) {
430
- usedSet.delete(id);
431
- pool.unshift(id);
432
441
  return;
433
442
  }
434
443
 
@@ -454,8 +463,8 @@
454
463
  getPostContainers(),
455
464
  Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3),
456
465
  normalizeBool(cfg.showFirstMessageAd),
457
- state.poolPosts,
458
- state.usedPosts
466
+ state.allPosts,
467
+ 'curPosts'
459
468
  );
460
469
  }
461
470
  } else if (kind === 'categoryTopics') {
@@ -465,8 +474,8 @@
465
474
  getTopicItems(),
466
475
  Math.max(1, parseInt(cfg.intervalPosts, 10) || 6),
467
476
  normalizeBool(cfg.showFirstTopicAd),
468
- state.poolTopics,
469
- state.usedTopics
477
+ state.allTopics,
478
+ 'curTopics'
470
479
  );
471
480
  }
472
481
  } else if (kind === 'categories') {
@@ -476,8 +485,8 @@
476
485
  getCategoryItems(),
477
486
  Math.max(1, parseInt(cfg.intervalCategories, 10) || 4),
478
487
  normalizeBool(cfg.showFirstCategoryAd),
479
- state.poolCategories,
480
- state.usedCategories
488
+ state.allCategories,
489
+ 'curCategories'
481
490
  );
482
491
  }
483
492
  }
@@ -508,17 +517,15 @@
508
517
 
509
518
  // reset state
510
519
  state.cfg = null;
511
- state.poolTopics = [];
512
- state.poolPosts = [];
513
- state.poolCategories = [];
514
- state.usedTopics.clear();
515
- state.usedPosts.clear();
516
- state.usedCategories.clear();
520
+ state.allTopics = [];
521
+ state.allPosts = [];
522
+ state.allCategories = [];
523
+ state.curTopics = 0;
524
+ state.curPosts = 0;
525
+ state.curCategories = 0;
517
526
  state.lastShowById.clear();
518
527
  state.heroDoneForPage = false;
519
528
 
520
- sessionDefinedIds.clear();
521
-
522
529
  // keep observers alive (MutationObserver will re-trigger after navigation)
523
530
  }
524
531