nodebb-plugin-ezoic-infinite 1.5.99 → 1.6.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.5.99",
3
+ "version": "1.6.1",
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,92 +1344,184 @@ function buildOrdinalMap(items) {
1344
1344
 
1345
1345
 
1346
1346
 
1347
- // ===== V8 between-cap-per-window =====
1347
+
1348
+ // ===== CLEAN REFRACTOR: visibility manager for Ezoic wraps =====
1348
1349
  (function () {
1349
- var ticking = false;
1350
- var lastRun = 0;
1351
- var RUN_COOLDOWN = 220; // ms
1350
+ var BETWEEN_SELECTOR = '.nodebb-ezoic-wrap.ezoic-ad-between';
1351
+ var MESSAGE_SELECTOR = '.nodebb-ezoic-wrap.ezoic-ad-message';
1352
+ var WRAP_SELECTOR = BETWEEN_SELECTOR + ', ' + MESSAGE_SELECTOR;
1352
1353
 
1353
- // Keep at most N between-wraps per vertical band around viewport
1354
- var BAND_PX = 900;
1355
- var MAX_PER_BAND = 2;
1354
+ var KEEP_MARGIN_BETWEEN_DESKTOP = 2600;
1355
+ var KEEP_MARGIN_BETWEEN_MOBILE = 1900;
1356
1356
 
1357
- function capBetweenWraps() {
1358
- var now = Date.now();
1359
- if (now - lastRun < RUN_COOLDOWN) return;
1360
- lastRun = now;
1357
+ var SHOW_COOLDOWN_MS = 1200;
1358
+ var MAX_SHOW_PER_TICK = 4;
1361
1359
 
1362
- var wraps;
1363
- try { wraps = Array.prototype.slice.call(document.querySelectorAll('.nodebb-ezoic-wrap.ezoic-ad-between')); }
1364
- catch (e) { return; }
1360
+ function isMobile() {
1361
+ try { return window.matchMedia && window.matchMedia('(max-width: 767px)').matches; } catch (e) { return false; }
1362
+ }
1363
+ function keepMarginBetween() { return isMobile() ? KEEP_MARGIN_BETWEEN_MOBILE : KEEP_MARGIN_BETWEEN_DESKTOP; }
1364
+
1365
+ var lastShowById = Object.create(null);
1366
+ var showQueue = [];
1367
+ var showTicking = false;
1365
1368
 
1366
- if (!wraps.length) return;
1369
+ function getWrapId(w) {
1370
+ try { return w.getAttribute('data-ezoic-wrapid'); } catch (e) { return null; }
1371
+ }
1372
+
1373
+ function enqueueShow(id) {
1374
+ if (!id) return;
1375
+ var now = Date.now();
1376
+ var last = lastShowById[id] || 0;
1377
+ if (now - last < SHOW_COOLDOWN_MS) return;
1378
+
1379
+ for (var i = 0; i < showQueue.length; i++) if (showQueue[i] === id) return;
1380
+ showQueue.push(id);
1381
+ scheduleShowTick();
1382
+ }
1367
1383
 
1368
- var y = window.pageYOffset || document.documentElement.scrollTop || 0;
1369
- var viewportTop = y;
1370
- var viewportBottom = y + (window.innerHeight || document.documentElement.clientHeight || 0);
1384
+ function scheduleShowTick() {
1385
+ if (showTicking) return;
1386
+ showTicking = true;
1387
+ requestAnimationFrame(function () {
1388
+ showTicking = false;
1389
+ var n = 0;
1390
+ while (showQueue.length && n < MAX_SHOW_PER_TICK) {
1391
+ var id = showQueue.shift();
1392
+ try {
1393
+ if (window.ezstandalone && typeof window.ezstandalone.showAds === 'function') {
1394
+ window.ezstandalone.showAds(String(id));
1395
+ lastShowById[id] = Date.now();
1396
+ }
1397
+ } catch (e) {}
1398
+ n++;
1399
+ }
1400
+ if (showQueue.length) scheduleShowTick();
1401
+ });
1402
+ }
1371
1403
 
1372
- // Priorité: conserver ce qui est proche/visible, limiter le reste par bande
1373
- var buckets = Object.create(null);
1404
+ function removeFarBetweenWraps() {
1405
+ var margin = keepMarginBetween();
1406
+ var removed = 0;
1407
+ var wraps;
1408
+ try { wraps = document.querySelectorAll(BETWEEN_SELECTOR); } catch (e) { return; }
1374
1409
 
1375
1410
  wraps.forEach(function (w) {
1411
+ if (removed >= 3) return;
1376
1412
  try {
1413
+ var lv = parseInt(w.getAttribute('data-last-visible') || '0', 10);
1414
+ if (lv && (Date.now() - lv) < 12000) return;
1415
+
1377
1416
  var r = w.getBoundingClientRect();
1378
- var absTop = y + r.top;
1379
- var absBottom = y + r.bottom;
1380
-
1381
- var inOrNearViewport = (absBottom >= viewportTop - 200) && (absTop <= viewportBottom + 200);
1382
- var band = Math.floor(absTop / BAND_PX);
1383
- var key = String(band);
1384
-
1385
- if (!buckets[key]) buckets[key] = [];
1386
- buckets[key].push({
1387
- node: w,
1388
- absTop: absTop,
1389
- absBottom: absBottom,
1390
- inOrNearViewport: inOrNearViewport
1391
- });
1417
+ if (r.bottom < -margin || r.top > ((window.innerHeight || 0) + margin)) {
1418
+ w.remove();
1419
+ removed += 1;
1420
+ }
1392
1421
  } catch (e) {}
1393
1422
  });
1423
+ }
1394
1424
 
1395
- Object.keys(buckets).forEach(function (k) {
1396
- var arr = buckets[k];
1397
- // garde d'abord proche/visible, puis les plus récents (data-created), puis position
1398
- arr.sort(function (a, b) {
1399
- if (a.inOrNearViewport !== b.inOrNearViewport) return a.inOrNearViewport ? -1 : 1;
1400
- var ac = parseInt((a.node.getAttribute('data-created') || '0'), 10);
1401
- var bc = parseInt((b.node.getAttribute('data-created') || '0'), 10);
1402
- if (ac !== bc) return bc - ac; // garder plus récent
1403
- return a.absTop - b.absTop;
1404
- });
1425
+ var io = null;
1426
+ function installIO() {
1427
+ if (io || typeof IntersectionObserver === 'undefined') return;
1405
1428
 
1406
- for (var i = MAX_PER_BAND; i < arr.length; i++) {
1407
- try { arr[i].node.remove(); } catch (e) {}
1408
- }
1429
+ io = new IntersectionObserver(function (entries) {
1430
+ try {
1431
+ entries.forEach(function (e) {
1432
+ if (!e || !e.target) return;
1433
+ if (e.isIntersecting) {
1434
+ try { e.target.setAttribute('data-last-visible', String(Date.now())); } catch (err) {}
1435
+
1436
+ var id = getWrapId(e.target);
1437
+ if (id) enqueueShow(id);
1438
+
1439
+ try {
1440
+ var ph = e.target.querySelector('[data-ezoic-id]');
1441
+ if (ph) enqueueShow(ph.getAttribute('data-ezoic-id'));
1442
+ } catch (err) {}
1443
+ }
1444
+ });
1445
+ } catch (e) {}
1446
+ }, { root: null, rootMargin: '900px 0px 900px 0px', threshold: 0.01 });
1447
+
1448
+ try { document.querySelectorAll(WRAP_SELECTOR).forEach(function (w) { try { io.observe(w); } catch(e) {} }); } catch (e) {}
1449
+ }
1450
+
1451
+ var moInstalled = false;
1452
+ function installMO() {
1453
+ if (moInstalled || typeof MutationObserver === 'undefined') return;
1454
+ moInstalled = true;
1455
+
1456
+ var mo = new MutationObserver(function (muts) {
1457
+ if (!io) return;
1458
+ try {
1459
+ for (var i = 0; i < muts.length; i++) {
1460
+ var m = muts[i];
1461
+ if (!m.addedNodes) continue;
1462
+ for (var j = 0; j < m.addedNodes.length; j++) {
1463
+ var n = m.addedNodes[j];
1464
+ if (!n || n.nodeType !== 1) continue;
1465
+
1466
+ if (n.matches && n.matches(WRAP_SELECTOR)) {
1467
+ try { io.observe(n); } catch (e) {}
1468
+ try { var id = getWrapId(n); if (id) enqueueShow(id); } catch (e) {}
1469
+ } else if (n.querySelectorAll) {
1470
+ var inner = n.querySelectorAll(WRAP_SELECTOR);
1471
+ for (var k = 0; k < inner.length; k++) {
1472
+ try { io.observe(inner[k]); } catch (e) {}
1473
+ }
1474
+ }
1475
+ }
1476
+ }
1477
+ } catch (e) {}
1409
1478
  });
1479
+
1480
+ try { mo.observe(document.documentElement || document.body, { childList: true, subtree: true }); } catch (e) {}
1410
1481
  }
1411
1482
 
1412
- function scheduleCap() {
1413
- if (ticking) return;
1414
- ticking = true;
1483
+ var sweepPending = false;
1484
+ var lastSweep = 0;
1485
+ var SWEEP_COOLDOWN_MS = 600;
1486
+
1487
+ function scheduleSweep() {
1488
+ var now = Date.now();
1489
+ if (now - lastSweep < SWEEP_COOLDOWN_MS) return;
1490
+ if (sweepPending) return;
1491
+ sweepPending = true;
1415
1492
  requestAnimationFrame(function () {
1416
- try { capBetweenWraps(); } catch (e) {}
1417
- ticking = false;
1493
+ sweepPending = false;
1494
+ lastSweep = Date.now();
1495
+ removeFarBetweenWraps();
1418
1496
  });
1419
1497
  }
1420
1498
 
1421
- window.addEventListener('scroll', scheduleCap, { passive: true });
1422
- window.addEventListener('resize', scheduleCap, { passive: true });
1499
+ function init() {
1500
+ installIO();
1501
+ installMO();
1423
1502
 
1424
- if (window.jQuery) {
1425
- window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function () {
1426
- setTimeout(scheduleCap, 0);
1427
- setTimeout(scheduleCap, 200);
1428
- setTimeout(scheduleCap, 700);
1429
- });
1503
+ window.addEventListener('scroll', function () {
1504
+ scheduleSweep();
1505
+ scheduleShowTick();
1506
+ }, { passive: true });
1507
+
1508
+ window.addEventListener('resize', function () {
1509
+ scheduleSweep();
1510
+ scheduleShowTick();
1511
+ }, { passive: true });
1512
+
1513
+ if (window.jQuery) {
1514
+ window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function () {
1515
+ setTimeout(function () { installIO(); scheduleSweep(); scheduleShowTick(); }, 0);
1516
+ });
1517
+ }
1518
+
1519
+ setTimeout(function () { installIO(); scheduleSweep(); scheduleShowTick(); }, 0);
1430
1520
  }
1431
1521
 
1432
- setTimeout(scheduleCap, 0);
1522
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
1523
+ else init();
1433
1524
  })();
1434
- // ===== /V8 =====
1525
+ // ===== /CLEAN REFRACTOR =====
1526
+
1435
1527
 
package/public/style.css CHANGED
@@ -79,3 +79,9 @@
79
79
  position: static !important;
80
80
  top: auto !important;
81
81
  }
82
+
83
+
84
+ /* ===== CLEAN REFRACTOR perf notes ===== */
85
+ .nodebb-ezoic-wrap { contain: content; }
86
+ /* ===== /CLEAN REFRACTOR ===== */
87
+