nodebb-plugin-ezoic-infinite 1.6.0 → 1.6.2

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 +135 -18
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
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
@@ -1344,28 +1344,93 @@ function buildOrdinalMap(items) {
1344
1344
 
1345
1345
 
1346
1346
 
1347
+
1348
+
1347
1349
  // ===== CLEAN REFRACTOR: visibility manager for Ezoic wraps =====
1348
1350
  (function () {
1349
- var WRAP_SELECTOR = '.nodebb-ezoic-wrap.ezoic-ad-between, .nodebb-ezoic-wrap.ezoic-ad-message';
1350
- var KEEP_MARGIN_PX_DESKTOP = 1600;
1351
- var KEEP_MARGIN_PX_MOBILE = 1100;
1352
- var MAX_REMOVALS_PER_SWEEP = 6;
1351
+ // v3 improvements:
1352
+ // - Faster global rendering: larger preload margin, higher show throughput.
1353
+ // - Reliable on UP-scroll: scroll-proximity scanner enqueues showAds for near-viewport wraps even if IO doesn't fire.
1354
+
1355
+ var BETWEEN_SELECTOR = '.nodebb-ezoic-wrap.ezoic-ad-between';
1356
+ var MESSAGE_SELECTOR = '.nodebb-ezoic-wrap.ezoic-ad-message';
1357
+ var WRAP_SELECTOR = BETWEEN_SELECTOR + ', ' + MESSAGE_SELECTOR;
1358
+
1359
+ // We never remove message wraps; only very conservative cleanup for between wraps.
1360
+ var KEEP_MARGIN_BETWEEN_DESKTOP = 3200;
1361
+ var KEEP_MARGIN_BETWEEN_MOBILE = 2300;
1362
+
1363
+ // Show tuning
1364
+ var SHOW_COOLDOWN_MS = 650; // faster re-show when coming back up
1365
+ var MAX_SHOW_PER_TICK = 10; // higher throughput for long scroll pages
1366
+
1367
+ // Proximity scan (fix up-scroll non-display)
1368
+ var PROXIMITY_SCAN_COOLDOWN_MS = 180;
1369
+ var PROXIMITY_MARGIN_DESKTOP = 1400; // px above+below viewport
1370
+ var PROXIMITY_MARGIN_MOBILE = 1100;
1353
1371
 
1354
1372
  function isMobile() {
1355
1373
  try { return window.matchMedia && window.matchMedia('(max-width: 767px)').matches; } catch (e) { return false; }
1356
1374
  }
1357
- function keepMargin() { return isMobile() ? KEEP_MARGIN_PX_MOBILE : KEEP_MARGIN_PX_DESKTOP; }
1375
+ function keepMarginBetween() { return isMobile() ? KEEP_MARGIN_BETWEEN_MOBILE : KEEP_MARGIN_BETWEEN_DESKTOP; }
1376
+ function proximityMargin() { return isMobile() ? PROXIMITY_MARGIN_MOBILE : PROXIMITY_MARGIN_DESKTOP; }
1377
+
1378
+ var lastShowById = Object.create(null);
1379
+ var showQueue = [];
1380
+ var showTicking = false;
1381
+
1382
+ function getWrapId(w) {
1383
+ try { return w.getAttribute('data-ezoic-wrapid'); } catch (e) { return null; }
1384
+ }
1385
+ function getPlaceholderId(w) {
1386
+ try {
1387
+ var ph = w.querySelector('[data-ezoic-id]');
1388
+ return ph ? ph.getAttribute('data-ezoic-id') : null;
1389
+ } catch (e) { return null; }
1390
+ }
1391
+
1392
+ function enqueueShow(id) {
1393
+ if (!id) return;
1394
+ var now = Date.now();
1395
+ var last = lastShowById[id] || 0;
1396
+ if (now - last < SHOW_COOLDOWN_MS) return;
1397
+
1398
+ for (var i = 0; i < showQueue.length; i++) if (showQueue[i] === id) return;
1399
+ showQueue.push(id);
1400
+ scheduleShowTick();
1401
+ }
1402
+
1403
+ function scheduleShowTick() {
1404
+ if (showTicking) return;
1405
+ showTicking = true;
1406
+ requestAnimationFrame(function () {
1407
+ showTicking = false;
1408
+ var n = 0;
1409
+ while (showQueue.length && n < MAX_SHOW_PER_TICK) {
1410
+ var id = showQueue.shift();
1411
+ try {
1412
+ if (window.ezstandalone && typeof window.ezstandalone.showAds === 'function') {
1413
+ window.ezstandalone.showAds(String(id));
1414
+ lastShowById[id] = Date.now();
1415
+ }
1416
+ } catch (e) {}
1417
+ n++;
1418
+ }
1419
+ if (showQueue.length) scheduleShowTick();
1420
+ });
1421
+ }
1358
1422
 
1359
- function removeFarWraps() {
1360
- var margin = keepMargin();
1423
+ function removeFarBetweenWraps() {
1424
+ var margin = keepMarginBetween();
1361
1425
  var removed = 0;
1362
1426
  var wraps;
1363
- try { wraps = document.querySelectorAll(WRAP_SELECTOR); } catch (e) { return; }
1427
+ try { wraps = document.querySelectorAll(BETWEEN_SELECTOR); } catch (e) { return; }
1428
+
1364
1429
  wraps.forEach(function (w) {
1365
- if (removed >= MAX_REMOVALS_PER_SWEEP) return;
1430
+ if (removed >= 3) return;
1366
1431
  try {
1367
1432
  var lv = parseInt(w.getAttribute('data-last-visible') || '0', 10);
1368
- if (lv && (Date.now() - lv) < 8000) return;
1433
+ if (lv && (Date.now() - lv) < 15000) return;
1369
1434
 
1370
1435
  var r = w.getBoundingClientRect();
1371
1436
  if (r.bottom < -margin || r.top > ((window.innerHeight || 0) + margin)) {
@@ -1376,26 +1441,33 @@ function buildOrdinalMap(items) {
1376
1441
  });
1377
1442
  }
1378
1443
 
1444
+ // IntersectionObserver: preload well before viewport
1379
1445
  var io = null;
1380
1446
  function installIO() {
1381
1447
  if (io || typeof IntersectionObserver === 'undefined') return;
1448
+
1382
1449
  io = new IntersectionObserver(function (entries) {
1383
1450
  try {
1384
1451
  entries.forEach(function (e) {
1385
- if (e && e.target && e.isIntersecting) {
1452
+ if (!e || !e.target) return;
1453
+ if (e.isIntersecting) {
1386
1454
  try { e.target.setAttribute('data-last-visible', String(Date.now())); } catch (err) {}
1455
+ var id = getWrapId(e.target) || getPlaceholderId(e.target);
1456
+ if (id) enqueueShow(id);
1387
1457
  }
1388
1458
  });
1389
1459
  } catch (e) {}
1390
- }, { root: null, rootMargin: '0px', threshold: 0.01 });
1460
+ }, { root: null, rootMargin: '1400px 0px 1400px 0px', threshold: 0.01 });
1391
1461
 
1392
1462
  try { document.querySelectorAll(WRAP_SELECTOR).forEach(function (w) { try { io.observe(w); } catch(e) {} }); } catch (e) {}
1393
1463
  }
1394
1464
 
1465
+ // Observe new wraps
1395
1466
  var moInstalled = false;
1396
1467
  function installMO() {
1397
1468
  if (moInstalled || typeof MutationObserver === 'undefined') return;
1398
1469
  moInstalled = true;
1470
+
1399
1471
  var mo = new MutationObserver(function (muts) {
1400
1472
  if (!io) return;
1401
1473
  try {
@@ -1405,8 +1477,11 @@ function buildOrdinalMap(items) {
1405
1477
  for (var j = 0; j < m.addedNodes.length; j++) {
1406
1478
  var n = m.addedNodes[j];
1407
1479
  if (!n || n.nodeType !== 1) continue;
1480
+
1408
1481
  if (n.matches && n.matches(WRAP_SELECTOR)) {
1409
1482
  try { io.observe(n); } catch (e) {}
1483
+ var id = getWrapId(n) || getPlaceholderId(n);
1484
+ if (id) enqueueShow(id);
1410
1485
  } else if (n.querySelectorAll) {
1411
1486
  var inner = n.querySelectorAll(WRAP_SELECTOR);
1412
1487
  for (var k = 0; k < inner.length; k++) {
@@ -1417,12 +1492,41 @@ function buildOrdinalMap(items) {
1417
1492
  }
1418
1493
  } catch (e) {}
1419
1494
  });
1495
+
1420
1496
  try { mo.observe(document.documentElement || document.body, { childList: true, subtree: true }); } catch (e) {}
1421
1497
  }
1422
1498
 
1499
+ // Proximity scanner: ensures re-show on up-scroll even if IO doesn't trigger (e.g. cached intersection state)
1500
+ var lastScan = 0;
1501
+ function proximityScan() {
1502
+ var now = Date.now();
1503
+ if (now - lastScan < PROXIMITY_SCAN_COOLDOWN_MS) return;
1504
+ lastScan = now;
1505
+
1506
+ var margin = proximityMargin();
1507
+ var vh = window.innerHeight || document.documentElement.clientHeight || 0;
1508
+
1509
+ var wraps;
1510
+ try { wraps = document.querySelectorAll(WRAP_SELECTOR); } catch (e) { return; }
1511
+
1512
+ var budget = 12; // don't scan too heavy
1513
+ for (var i = 0; i < wraps.length && budget > 0; i++) {
1514
+ var w = wraps[i];
1515
+ try {
1516
+ var r = w.getBoundingClientRect();
1517
+ if (r.bottom >= -margin && r.top <= (vh + margin)) {
1518
+ var id = getWrapId(w) || getPlaceholderId(w);
1519
+ if (id) enqueueShow(id);
1520
+ budget--;
1521
+ }
1522
+ } catch (e) {}
1523
+ }
1524
+ }
1525
+
1526
+ // Sweep only between cleanup
1423
1527
  var sweepPending = false;
1424
1528
  var lastSweep = 0;
1425
- var SWEEP_COOLDOWN_MS = 250;
1529
+ var SWEEP_COOLDOWN_MS = 700;
1426
1530
 
1427
1531
  function scheduleSweep() {
1428
1532
  var now = Date.now();
@@ -1432,22 +1536,33 @@ function buildOrdinalMap(items) {
1432
1536
  requestAnimationFrame(function () {
1433
1537
  sweepPending = false;
1434
1538
  lastSweep = Date.now();
1435
- removeFarWraps();
1539
+ removeFarBetweenWraps();
1436
1540
  });
1437
1541
  }
1438
1542
 
1543
+ function onScroll() {
1544
+ scheduleSweep();
1545
+ proximityScan();
1546
+ scheduleShowTick();
1547
+ }
1548
+
1439
1549
  function init() {
1440
1550
  installIO();
1441
1551
  installMO();
1442
- window.addEventListener('scroll', scheduleSweep, { passive: true });
1443
- window.addEventListener('resize', scheduleSweep, { passive: true });
1552
+
1553
+ window.addEventListener('scroll', onScroll, { passive: true });
1554
+ window.addEventListener('resize', onScroll, { passive: true });
1444
1555
 
1445
1556
  if (window.jQuery) {
1446
1557
  window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function () {
1447
- setTimeout(function () { installIO(); scheduleSweep(); }, 0);
1558
+ setTimeout(function () {
1559
+ installIO();
1560
+ onScroll();
1561
+ }, 0);
1448
1562
  });
1449
1563
  }
1450
- setTimeout(function () { installIO(); scheduleSweep(); }, 0);
1564
+
1565
+ setTimeout(function () { installIO(); onScroll(); }, 0);
1451
1566
  }
1452
1567
 
1453
1568
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
@@ -1455,3 +1570,5 @@ function buildOrdinalMap(items) {
1455
1570
  })();
1456
1571
  // ===== /CLEAN REFRACTOR =====
1457
1572
 
1573
+
1574
+