nodebb-plugin-ezoic-infinite 1.6.4 → 1.6.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.
- package/package.json +1 -1
- package/public/client.js +101 -159
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1347,54 +1347,59 @@ function buildOrdinalMap(items) {
|
|
|
1347
1347
|
|
|
1348
1348
|
|
|
1349
1349
|
|
|
1350
|
+
|
|
1350
1351
|
// ===== CLEAN REFRACTOR: visibility manager for Ezoic wraps =====
|
|
1351
1352
|
(function () {
|
|
1352
|
-
// v2.
|
|
1353
|
-
//
|
|
1354
|
-
// -
|
|
1355
|
-
//
|
|
1353
|
+
// v2.3 (no-redefine):
|
|
1354
|
+
// Your logs show:
|
|
1355
|
+
// - "Placeholder Id XXX has already been defined"
|
|
1356
|
+
// - "No valid placeholders for loadMore"
|
|
1357
|
+
//
|
|
1358
|
+
// Root cause: calling showAds repeatedly for the same placeholder id and/or
|
|
1359
|
+
// removing/recreating wrappers with the same IDs causes Ezoic to treat them as re-defined.
|
|
1360
|
+
//
|
|
1361
|
+
// Fix:
|
|
1362
|
+
// - NEVER remove wraps/placeholders here (no DOM deletion of ads).
|
|
1363
|
+
// - Only call showAds ONCE per placeholder per page lifetime (unless the placeholder is truly empty).
|
|
1364
|
+
// - De-duplicate placeholders in DOM: if the same data-ezoic-id appears multiple times, keep the first.
|
|
1356
1365
|
|
|
1357
1366
|
var BETWEEN_SELECTOR = '.nodebb-ezoic-wrap.ezoic-ad-between';
|
|
1358
1367
|
var MESSAGE_SELECTOR = '.nodebb-ezoic-wrap.ezoic-ad-message';
|
|
1359
1368
|
var WRAP_SELECTOR = BETWEEN_SELECTOR + ', ' + MESSAGE_SELECTOR;
|
|
1360
1369
|
|
|
1361
|
-
|
|
1362
|
-
var
|
|
1363
|
-
|
|
1364
|
-
// Show tuning (safe/moderate)
|
|
1365
|
-
var SHOW_COOLDOWN_MS = 900;
|
|
1366
|
-
var MAX_SHOW_PER_TICK = 6;
|
|
1367
|
-
|
|
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
|
|
1370
|
+
// show tuning (safe)
|
|
1371
|
+
var MAX_SHOW_PER_TICK = 4;
|
|
1373
1372
|
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
}
|
|
1377
|
-
function keepMarginBetween() { return isMobile() ? KEEP_MARGIN_BETWEEN_MOBILE : KEEP_MARGIN_BETWEEN_DESKTOP; }
|
|
1378
|
-
|
|
1379
|
-
var lastShowById = Object.create(null);
|
|
1373
|
+
// internal state
|
|
1374
|
+
var activatedById = Object.create(null); // id -> ts
|
|
1380
1375
|
var showQueue = [];
|
|
1381
1376
|
var showTicking = false;
|
|
1382
1377
|
|
|
1383
|
-
function
|
|
1378
|
+
function getIdFromWrap(w) {
|
|
1384
1379
|
try { return w.getAttribute('data-ezoic-wrapid'); } catch (e) { return null; }
|
|
1385
1380
|
}
|
|
1386
|
-
function
|
|
1381
|
+
function getIdFromPlaceholder(w) {
|
|
1387
1382
|
try {
|
|
1388
1383
|
var ph = w.querySelector('[data-ezoic-id]');
|
|
1389
1384
|
return ph ? ph.getAttribute('data-ezoic-id') : null;
|
|
1390
1385
|
} catch (e) { return null; }
|
|
1391
1386
|
}
|
|
1387
|
+
function getId(w) { return getIdFromWrap(w) || getIdFromPlaceholder(w); }
|
|
1388
|
+
|
|
1389
|
+
function isFilled(w) {
|
|
1390
|
+
try {
|
|
1391
|
+
// if Ezoic/Google already injected, there will be an iframe or an element with id starting google_ads_iframe
|
|
1392
|
+
if (w.querySelector('iframe')) return true;
|
|
1393
|
+
if (w.querySelector('[id^="google_ads_iframe"]')) return true;
|
|
1394
|
+
if (w.querySelector('.ezoic-ad')) return true;
|
|
1395
|
+
} catch (e) {}
|
|
1396
|
+
return false;
|
|
1397
|
+
}
|
|
1392
1398
|
|
|
1393
1399
|
function enqueueShow(id) {
|
|
1394
1400
|
if (!id) return;
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
if (now - last < SHOW_COOLDOWN_MS) return;
|
|
1401
|
+
// show only once (avoid "already defined")
|
|
1402
|
+
if (activatedById[id]) return;
|
|
1398
1403
|
|
|
1399
1404
|
for (var i = 0; i < showQueue.length; i++) if (showQueue[i] === id) return;
|
|
1400
1405
|
showQueue.push(id);
|
|
@@ -1410,9 +1415,9 @@ function buildOrdinalMap(items) {
|
|
|
1410
1415
|
while (showQueue.length && n < MAX_SHOW_PER_TICK) {
|
|
1411
1416
|
var id = showQueue.shift();
|
|
1412
1417
|
try {
|
|
1413
|
-
if (window.ezstandalone && typeof window.ezstandalone.showAds === 'function') {
|
|
1418
|
+
if (!activatedById[id] && window.ezstandalone && typeof window.ezstandalone.showAds === 'function') {
|
|
1414
1419
|
window.ezstandalone.showAds(String(id));
|
|
1415
|
-
|
|
1420
|
+
activatedById[id] = Date.now();
|
|
1416
1421
|
}
|
|
1417
1422
|
} catch (e) {}
|
|
1418
1423
|
n++;
|
|
@@ -1421,28 +1426,27 @@ function buildOrdinalMap(items) {
|
|
|
1421
1426
|
});
|
|
1422
1427
|
}
|
|
1423
1428
|
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
var
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
});
|
|
1429
|
+
// De-duplicate placeholders to avoid Ezoic warnings.
|
|
1430
|
+
function dedupePlaceholders() {
|
|
1431
|
+
var seen = Object.create(null);
|
|
1432
|
+
try {
|
|
1433
|
+
document.querySelectorAll('.nodebb-ezoic-wrap [data-ezoic-id]').forEach(function (ph) {
|
|
1434
|
+
try {
|
|
1435
|
+
var id = ph.getAttribute('data-ezoic-id');
|
|
1436
|
+
if (!id) return;
|
|
1437
|
+
if (seen[id]) {
|
|
1438
|
+
// remove duplicate wrapper entirely (keep first occurrence)
|
|
1439
|
+
var wrap = ph.closest('.nodebb-ezoic-wrap');
|
|
1440
|
+
if (wrap) wrap.remove();
|
|
1441
|
+
} else {
|
|
1442
|
+
seen[id] = true;
|
|
1443
|
+
}
|
|
1444
|
+
} catch (e) {}
|
|
1445
|
+
});
|
|
1446
|
+
} catch (e) {}
|
|
1443
1447
|
}
|
|
1444
1448
|
|
|
1445
|
-
//
|
|
1449
|
+
// Track visibility: when a wrap comes near viewport, trigger show once if empty.
|
|
1446
1450
|
var io = null;
|
|
1447
1451
|
function installIO() {
|
|
1448
1452
|
if (io || typeof IntersectionObserver === 'undefined') return;
|
|
@@ -1452,9 +1456,15 @@ function buildOrdinalMap(items) {
|
|
|
1452
1456
|
entries.forEach(function (e) {
|
|
1453
1457
|
if (!e || !e.target) return;
|
|
1454
1458
|
if (e.isIntersecting) {
|
|
1455
|
-
|
|
1456
|
-
var id =
|
|
1457
|
-
if (id)
|
|
1459
|
+
// If the slot is already filled, mark it as activated to prevent re-define attempts.
|
|
1460
|
+
var id = getId(e.target);
|
|
1461
|
+
if (!id) return;
|
|
1462
|
+
|
|
1463
|
+
if (isFilled(e.target)) {
|
|
1464
|
+
activatedById[id] = activatedById[id] || Date.now();
|
|
1465
|
+
return;
|
|
1466
|
+
}
|
|
1467
|
+
enqueueShow(id);
|
|
1458
1468
|
}
|
|
1459
1469
|
});
|
|
1460
1470
|
} catch (e) {}
|
|
@@ -1463,12 +1473,16 @@ function buildOrdinalMap(items) {
|
|
|
1463
1473
|
try { document.querySelectorAll(WRAP_SELECTOR).forEach(function (w) { try { io.observe(w); } catch(e) {} }); } catch (e) {}
|
|
1464
1474
|
}
|
|
1465
1475
|
|
|
1476
|
+
// Observe newly added wraps and observe them + dedupe
|
|
1466
1477
|
var moInstalled = false;
|
|
1467
1478
|
function installMO() {
|
|
1468
1479
|
if (moInstalled || typeof MutationObserver === 'undefined') return;
|
|
1469
1480
|
moInstalled = true;
|
|
1470
1481
|
|
|
1471
1482
|
var mo = new MutationObserver(function (muts) {
|
|
1483
|
+
// dedupe quickly, then observe new wraps
|
|
1484
|
+
try { dedupePlaceholders(); } catch (e) {}
|
|
1485
|
+
|
|
1472
1486
|
if (!io) return;
|
|
1473
1487
|
try {
|
|
1474
1488
|
for (var i = 0; i < muts.length; i++) {
|
|
@@ -1480,8 +1494,10 @@ function buildOrdinalMap(items) {
|
|
|
1480
1494
|
|
|
1481
1495
|
if (n.matches && n.matches(WRAP_SELECTOR)) {
|
|
1482
1496
|
try { io.observe(n); } catch (e) {}
|
|
1483
|
-
|
|
1484
|
-
|
|
1497
|
+
// eager show if empty
|
|
1498
|
+
var id = getId(n);
|
|
1499
|
+
if (id && !isFilled(n)) enqueueShow(id);
|
|
1500
|
+
else if (id) activatedById[id] = activatedById[id] || Date.now();
|
|
1485
1501
|
} else if (n.querySelectorAll) {
|
|
1486
1502
|
var inner = n.querySelectorAll(WRAP_SELECTOR);
|
|
1487
1503
|
for (var k = 0; k < inner.length; k++) {
|
|
@@ -1496,122 +1512,47 @@ function buildOrdinalMap(items) {
|
|
|
1496
1512
|
try { mo.observe(document.documentElement || document.body, { childList: true, subtree: true }); } catch (e) {}
|
|
1497
1513
|
}
|
|
1498
1514
|
|
|
1499
|
-
function
|
|
1500
|
-
|
|
1501
|
-
|
|
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() {
|
|
1517
|
-
var now = Date.now();
|
|
1518
|
-
if (now - lastWalk < WALK_COOLDOWN_MS) return;
|
|
1519
|
-
lastWalk = now;
|
|
1520
|
-
|
|
1521
|
-
if (typeof document.elementFromPoint !== 'function') return;
|
|
1522
|
-
|
|
1523
|
-
var vh = window.innerHeight || document.documentElement.clientHeight || 0;
|
|
1524
|
-
if (!vh) return;
|
|
1525
|
-
|
|
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));
|
|
1515
|
+
function init() {
|
|
1516
|
+
// 1) dedupe existing
|
|
1517
|
+
dedupePlaceholders();
|
|
1548
1518
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
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
|
-
}
|
|
1519
|
+
// 2) install observers
|
|
1520
|
+
installIO();
|
|
1521
|
+
installMO();
|
|
1563
1522
|
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1523
|
+
// 3) initial eager show for empty visible-ish wraps (bounded)
|
|
1524
|
+
try {
|
|
1525
|
+
var vh = window.innerHeight || document.documentElement.clientHeight || 0;
|
|
1526
|
+
var margin = 900;
|
|
1527
|
+
var wraps = document.querySelectorAll(WRAP_SELECTOR);
|
|
1528
|
+
var budget = 12;
|
|
1529
|
+
for (var i = 0; i < wraps.length && budget > 0; i++) {
|
|
1530
|
+
var w = wraps[i];
|
|
1531
|
+
var r = w.getBoundingClientRect();
|
|
1532
|
+
if (r.bottom >= -margin && r.top <= (vh + margin)) {
|
|
1533
|
+
var id = getId(w);
|
|
1534
|
+
if (id) {
|
|
1535
|
+
if (isFilled(w)) activatedById[id] = activatedById[id] || Date.now();
|
|
1536
|
+
else enqueueShow(id);
|
|
1572
1537
|
}
|
|
1573
|
-
|
|
1574
|
-
|
|
1538
|
+
budget--;
|
|
1539
|
+
}
|
|
1575
1540
|
}
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
var sweepPending = false;
|
|
1580
|
-
var lastSweep = 0;
|
|
1581
|
-
var SWEEP_COOLDOWN_MS = 600;
|
|
1582
|
-
|
|
1583
|
-
function scheduleSweep() {
|
|
1584
|
-
var now = Date.now();
|
|
1585
|
-
if (now - lastSweep < SWEEP_COOLDOWN_MS) return;
|
|
1586
|
-
if (sweepPending) return;
|
|
1587
|
-
sweepPending = true;
|
|
1588
|
-
requestAnimationFrame(function () {
|
|
1589
|
-
sweepPending = false;
|
|
1590
|
-
lastSweep = Date.now();
|
|
1591
|
-
removeFarBetweenWraps();
|
|
1592
|
-
});
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
function onScroll() {
|
|
1596
|
-
scheduleSweep();
|
|
1597
|
-
viewportWalkEnqueue();
|
|
1598
|
-
scheduleShowTick();
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
function init() {
|
|
1602
|
-
installIO();
|
|
1603
|
-
installMO();
|
|
1541
|
+
} catch (e) {}
|
|
1604
1542
|
|
|
1605
|
-
window.addEventListener('scroll',
|
|
1606
|
-
window.addEventListener('resize',
|
|
1543
|
+
window.addEventListener('scroll', scheduleShowTick, { passive: true });
|
|
1544
|
+
window.addEventListener('resize', scheduleShowTick, { passive: true });
|
|
1607
1545
|
|
|
1608
1546
|
if (window.jQuery) {
|
|
1609
1547
|
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function () {
|
|
1610
|
-
setTimeout(function () {
|
|
1548
|
+
setTimeout(function () {
|
|
1549
|
+
dedupePlaceholders();
|
|
1550
|
+
scheduleShowTick();
|
|
1551
|
+
}, 0);
|
|
1611
1552
|
});
|
|
1612
1553
|
}
|
|
1613
1554
|
|
|
1614
|
-
setTimeout(
|
|
1555
|
+
setTimeout(scheduleShowTick, 0);
|
|
1615
1556
|
}
|
|
1616
1557
|
|
|
1617
1558
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
@@ -1622,3 +1563,4 @@ function buildOrdinalMap(items) {
|
|
|
1622
1563
|
|
|
1623
1564
|
|
|
1624
1565
|
|
|
1566
|
+
|