nodebb-plugin-ezoic-infinite 1.6.3 → 1.6.4

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 +86 -29
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.6.3",
3
+ "version": "1.6.4",
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
@@ -1346,13 +1346,13 @@ function buildOrdinalMap(items) {
1346
1346
 
1347
1347
 
1348
1348
 
1349
+
1349
1350
  // ===== CLEAN REFRACTOR: visibility manager for Ezoic wraps =====
1350
1351
  (function () {
1351
- // v2.1 (safe):
1352
- // - Keep the v2 behavior that didn't break forum rendering.
1353
- // - Improve speed moderately (earlier preload + slightly higher show throughput).
1354
- // - Improve up-scroll reliability without scanning the whole DOM:
1355
- // * on scroll, enqueue showAds for wraps currently in/near viewport (small bounded loop).
1352
+ // v2.2:
1353
+ // - Fix "after long scroll: no ads down / no ads up" caused by O(n) scans with a small budget.
1354
+ // - Replace the near-viewport scan with a viewport-walk using elementFromPoint + sibling traversal.
1355
+ // This stays O(k) even with thousands of posts.
1356
1356
 
1357
1357
  var BETWEEN_SELECTOR = '.nodebb-ezoic-wrap.ezoic-ad-between';
1358
1358
  var MESSAGE_SELECTOR = '.nodebb-ezoic-wrap.ezoic-ad-message';
@@ -1361,14 +1361,15 @@ function buildOrdinalMap(items) {
1361
1361
  var KEEP_MARGIN_BETWEEN_DESKTOP = 2600;
1362
1362
  var KEEP_MARGIN_BETWEEN_MOBILE = 1900;
1363
1363
 
1364
- // Show tuning (moderate)
1364
+ // Show tuning (safe/moderate)
1365
1365
  var SHOW_COOLDOWN_MS = 900;
1366
1366
  var MAX_SHOW_PER_TICK = 6;
1367
1367
 
1368
- // Up-scroll helper: only check near-viewport wraps, bounded
1369
- var SCAN_COOLDOWN_MS = 220;
1370
- var lastScan = 0;
1371
- var SCAN_BUDGET = 10;
1368
+ // Viewport-walk tuning
1369
+ var WALK_COOLDOWN_MS = 180;
1370
+ var lastWalk = 0;
1371
+ var WALK_STEPS = 28; // siblings in each direction
1372
+ var WALK_POINTS = 2; // sample top & bottom area of viewport
1372
1373
 
1373
1374
  function isMobile() {
1374
1375
  try { return window.matchMedia && window.matchMedia('(max-width: 767px)').matches; } catch (e) { return false; }
@@ -1495,28 +1496,83 @@ function buildOrdinalMap(items) {
1495
1496
  try { mo.observe(document.documentElement || document.body, { childList: true, subtree: true }); } catch (e) {}
1496
1497
  }
1497
1498
 
1498
- // Small bounded scan near viewport on scroll (helps on up-scroll)
1499
- function scanNearViewport() {
1499
+ function closestWrap(el) {
1500
+ try {
1501
+ while (el && el !== document.body && el !== document.documentElement) {
1502
+ if (el.classList && el.classList.contains('nodebb-ezoic-wrap')) return el;
1503
+ el = el.parentElement;
1504
+ }
1505
+ } catch (e) {}
1506
+ return null;
1507
+ }
1508
+
1509
+ function enqueueWrap(w) {
1510
+ if (!w) return;
1511
+ var id = getWrapId(w) || getPlaceholderId(w);
1512
+ if (id) enqueueShow(id);
1513
+ }
1514
+
1515
+ // Viewport-walk: find content near viewport and walk siblings to pick nearby wraps.
1516
+ function viewportWalkEnqueue() {
1500
1517
  var now = Date.now();
1501
- if (now - lastScan < SCAN_COOLDOWN_MS) return;
1502
- lastScan = now;
1518
+ if (now - lastWalk < WALK_COOLDOWN_MS) return;
1519
+ lastWalk = now;
1520
+
1521
+ if (typeof document.elementFromPoint !== 'function') return;
1503
1522
 
1504
1523
  var vh = window.innerHeight || document.documentElement.clientHeight || 0;
1505
- var margin = 900; // near viewport window
1506
- var wraps;
1507
- try { wraps = document.querySelectorAll(WRAP_SELECTOR); } catch (e) { return; }
1524
+ if (!vh) return;
1508
1525
 
1509
- var budget = SCAN_BUDGET;
1510
- for (var i = 0; i < wraps.length && budget > 0; i++) {
1511
- var w = wraps[i];
1512
- try {
1513
- var r = w.getBoundingClientRect();
1514
- if (r.bottom >= -margin && r.top <= (vh + margin)) {
1515
- var id = getWrapId(w) || getPlaceholderId(w);
1516
- if (id) enqueueShow(id);
1517
- budget--;
1518
- }
1519
- } catch (e) {}
1526
+ // sample points: near top and near bottom
1527
+ var ys = [Math.min(vh - 10, 140), Math.max(10, vh - 180)];
1528
+ if (WALK_POINTS === 1) ys = [Math.min(vh - 10, 180)];
1529
+
1530
+ for (var p = 0; p < ys.length; p++) {
1531
+ var y = ys[p];
1532
+ var el = null;
1533
+ try { el = document.elementFromPoint(10, y); } catch (e) {}
1534
+ if (!el) continue;
1535
+
1536
+ // ascend to a stable list item, then traverse siblings
1537
+ var cursor = el;
1538
+ // try to find an item container to traverse in topic lists
1539
+ for (var i = 0; i < 8 && cursor && cursor.parentElement; i++) {
1540
+ if (cursor.classList && (cursor.classList.contains('topic-item') || cursor.classList.contains('posts-list') || cursor.classList.contains('category-item'))) break;
1541
+ cursor = cursor.parentElement;
1542
+ }
1543
+ // if cursor isn't traversable, just use the element itself
1544
+ cursor = cursor || el;
1545
+
1546
+ // find nearest wrap around this point
1547
+ enqueueWrap(closestWrap(el));
1548
+
1549
+ // walk next/prev siblings and enqueue wraps found
1550
+ var forward = cursor;
1551
+ for (var s = 0; s < WALK_STEPS; s++) {
1552
+ if (!forward) break;
1553
+ // check within forward node for wraps
1554
+ try {
1555
+ if (forward.matches && forward.matches(WRAP_SELECTOR)) enqueueWrap(forward);
1556
+ if (forward.querySelectorAll) {
1557
+ var w1 = forward.querySelectorAll(WRAP_SELECTOR);
1558
+ for (var wi = 0; wi < w1.length; wi++) enqueueWrap(w1[wi]);
1559
+ }
1560
+ } catch (e) {}
1561
+ forward = forward.nextElementSibling;
1562
+ }
1563
+
1564
+ var backward = cursor;
1565
+ for (var s2 = 0; s2 < WALK_STEPS; s2++) {
1566
+ if (!backward) break;
1567
+ try {
1568
+ if (backward.matches && backward.matches(WRAP_SELECTOR)) enqueueWrap(backward);
1569
+ if (backward.querySelectorAll) {
1570
+ var w2 = backward.querySelectorAll(WRAP_SELECTOR);
1571
+ for (var wj = 0; wj < w2.length; wj++) enqueueWrap(w2[wj]);
1572
+ }
1573
+ } catch (e) {}
1574
+ backward = backward.previousElementSibling;
1575
+ }
1520
1576
  }
1521
1577
  }
1522
1578
 
@@ -1538,7 +1594,7 @@ function buildOrdinalMap(items) {
1538
1594
 
1539
1595
  function onScroll() {
1540
1596
  scheduleSweep();
1541
- scanNearViewport();
1597
+ viewportWalkEnqueue();
1542
1598
  scheduleShowTick();
1543
1599
  }
1544
1600
 
@@ -1565,3 +1621,4 @@ function buildOrdinalMap(items) {
1565
1621
 
1566
1622
 
1567
1623
 
1624
+