nodebb-plugin-ezoic-infinite 1.6.9 → 1.6.11
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 +167 -263
- package/public/style.css +5 -3
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
function createWrapHostAndWrap(kindClass, targetEl) {
|
|
5
|
+
// For <ul>/<ol> we MUST insert a <li> to keep valid DOM.
|
|
6
|
+
// But for compatibility with existing selectors (often 'div.nodebb-ezoic-wrap'),
|
|
7
|
+
// we keep the actual wrap as a DIV inside the LI.
|
|
8
|
+
var host = null;
|
|
9
|
+
try {
|
|
10
|
+
if (kindClass === 'ezoic-ad-between') {
|
|
11
|
+
var p = targetEl && targetEl.parentElement;
|
|
12
|
+
if (p && (p.tagName === 'UL' || p.tagName === 'OL')) {
|
|
13
|
+
host = document.createElement('li');
|
|
14
|
+
host.className = 'nodebb-ezoic-host';
|
|
15
|
+
host.setAttribute('role', 'listitem');
|
|
16
|
+
host.style.listStyle = 'none';
|
|
17
|
+
host.style.width = '100%';
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
} catch (e) {}
|
|
21
|
+
|
|
22
|
+
var pair = createWrapHostAndWrap(kindClass, el);
|
|
23
|
+
var host = pair.hostEl;
|
|
24
|
+
var wrap = pair.wrapEl;
|
|
25
|
+
wrap.className = 'nodebb-ezoic-wrap ' + kindClass;
|
|
26
|
+
|
|
27
|
+
if (host) {
|
|
28
|
+
host.appendChild(wrap);
|
|
29
|
+
return { hostEl: host, wrapEl: wrap };
|
|
30
|
+
}
|
|
31
|
+
return { hostEl: wrap, wrapEl: wrap };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
4
35
|
// Track scroll direction to avoid aggressive recycling when the user scrolls upward.
|
|
5
36
|
// Recycling while scrolling up is a common cause of ads "bunching" and a "disappearing too fast" feeling.
|
|
6
37
|
let lastScrollY = 0;
|
|
@@ -637,7 +668,9 @@ function globalGapFixInit() {
|
|
|
637
668
|
// ---------------- insertion primitives ----------------
|
|
638
669
|
|
|
639
670
|
function buildWrap(id, kindClass, afterPos, createPlaceholder) {
|
|
640
|
-
const
|
|
671
|
+
const pair = createWrapHostAndWrap(kindClass, el);
|
|
672
|
+
const host = pair.hostEl;
|
|
673
|
+
const wrap = pair.wrapEl;
|
|
641
674
|
wrap.className = `${WRAP_CLASS} ${kindClass}`;
|
|
642
675
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
643
676
|
wrap.setAttribute('data-ezoic-wrapid', String(id));
|
|
@@ -704,13 +737,144 @@ function globalGapFixInit() {
|
|
|
704
737
|
}
|
|
705
738
|
|
|
706
739
|
function pruneOrphanWraps(kindClass, items) {
|
|
740
|
+
// Topic pages can be virtualized (posts removed from DOM as you scroll).
|
|
741
|
+
// When that happens, previously-inserted ad wraps may become "orphan" nodes with no
|
|
742
|
+
// nearby post containers, which leads to ads clustering together when scrolling back up.
|
|
743
|
+
// We prune only *true* orphans that are far offscreen to keep the UI stable.
|
|
744
|
+
if (!items || !items.length) return 0;
|
|
745
|
+
const itemSet = new Set(items);
|
|
746
|
+
const wraps = document.querySelectorAll(`.${WRAP_CLASS}.${kindClass}`);
|
|
747
|
+
let removed = 0;
|
|
748
|
+
|
|
749
|
+
const isFilled = (wrap) => {
|
|
750
|
+
return !!(wrap.querySelector('iframe, ins, img, video, [data-google-container-id]'));
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
const hasNearbyItem = (wrap) => {
|
|
754
|
+
// NodeBB/skins can inject separators/spacers; be tolerant.
|
|
755
|
+
let prev = wrap.previousElementSibling;
|
|
756
|
+
for (let i = 0; i < 14 && prev; i++) {
|
|
757
|
+
if (itemSet.has(prev)) return true;
|
|
758
|
+
prev = prev.previousElementSibling;
|
|
759
|
+
}
|
|
760
|
+
let next = wrap.nextElementSibling;
|
|
761
|
+
for (let i = 0; i < 14 && next; i++) {
|
|
762
|
+
if (itemSet.has(next)) return true;
|
|
763
|
+
next = next.nextElementSibling;
|
|
764
|
+
}
|
|
765
|
+
return false;
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
wraps.forEach((wrap) => {
|
|
769
|
+
// Never prune pinned placements.
|
|
770
|
+
try {
|
|
771
|
+
if (wrap.getAttribute('data-ezoic-pin') === '1') return;
|
|
772
|
+
} catch (e) {}
|
|
773
|
+
|
|
774
|
+
// For message/topic pages we may prune filled or empty orphans if they are far away,
|
|
775
|
+
// otherwise consecutive "stacks" can appear when posts are virtualized.
|
|
776
|
+
const isMessage = (kindClass === 'ezoic-ad-message');
|
|
777
|
+
if (!isMessage && isFilled(wrap)) return; // never prune filled ads for non-message lists
|
|
778
|
+
|
|
779
|
+
// Never prune a fresh wrap: it may fill late.
|
|
780
|
+
try {
|
|
781
|
+
const created = parseInt(wrap.getAttribute('data-created') || '0', 10);
|
|
782
|
+
if (created && (now() - created) < keepEmptyWrapMs()) return;
|
|
783
|
+
} catch (e) {}
|
|
784
|
+
|
|
785
|
+
if (hasNearbyItem(wrap)) {
|
|
786
|
+
try { wrap.classList && wrap.classList.remove('ez-orphan-hidden'); wrap.style && (wrap.style.display = ''); } catch (e) {}
|
|
707
787
|
return;
|
|
708
788
|
}
|
|
709
789
|
|
|
710
|
-
|
|
711
|
-
|
|
790
|
+
// If the anchor item is no longer in the DOM (virtualized), hide the wrap so ads never "stack"
|
|
791
|
+
// back-to-back while scrolling. We'll recycle it when its anchor comes back.
|
|
792
|
+
try { wrap.classList && wrap.classList.add('ez-orphan-hidden'); wrap.style && (wrap.style.display = 'none'); } catch (e) {}
|
|
793
|
+
|
|
794
|
+
// For message ads: only release if far offscreen to avoid perceived "vanishing" during fast scroll.
|
|
795
|
+
if (isMessage) {
|
|
796
|
+
try {
|
|
797
|
+
const r = wrap.getBoundingClientRect();
|
|
798
|
+
const vh = Math.max(1, window.innerHeight || 1);
|
|
799
|
+
const farAbove = r.bottom < -vh * 2;
|
|
800
|
+
const farBelow = r.top > vh * 4;
|
|
801
|
+
if (!farAbove && !farBelow) return;
|
|
802
|
+
} catch (e) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
712
805
|
}
|
|
713
806
|
|
|
807
|
+
withInternalDomChange(() => releaseWrapNode(wrap));
|
|
808
|
+
removed++;
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
return removed;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function decluster(kindClass) {
|
|
815
|
+
// Remove "near-consecutive" wraps (keep the first). Be tolerant of spacer nodes.
|
|
816
|
+
const wraps = Array.from(document.querySelectorAll(`.${WRAP_CLASS}.${kindClass}`));
|
|
817
|
+
if (wraps.length < 2) return 0;
|
|
818
|
+
|
|
819
|
+
const isWrap = (el) => !!(el && el.classList && el.classList.contains(WRAP_CLASS));
|
|
820
|
+
|
|
821
|
+
const isFilled = (wrap) => {
|
|
822
|
+
return !!(wrap && wrap.querySelector && wrap.querySelector('iframe, ins, img, video, [data-google-container-id]'));
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
const isFresh = (wrap) => {
|
|
826
|
+
try {
|
|
827
|
+
const created = parseInt(wrap.getAttribute('data-created') || '0', 10);
|
|
828
|
+
return created && (now() - created) < keepEmptyWrapMs();
|
|
829
|
+
} catch (e) {
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
let removed = 0;
|
|
835
|
+
for (const w of wraps) {
|
|
836
|
+
// Never decluster pinned placements.
|
|
837
|
+
try {
|
|
838
|
+
if (w.getAttribute && w.getAttribute('data-ezoic-pin') === '1') continue;
|
|
839
|
+
} catch (e) {}
|
|
840
|
+
|
|
841
|
+
let prev = w.previousElementSibling;
|
|
842
|
+
for (let i = 0; i < 3 && prev; i++) {
|
|
843
|
+
if (isWrap(prev)) {
|
|
844
|
+
// If the previous wrap is pinned, keep this one (spacing is intentional).
|
|
845
|
+
try {
|
|
846
|
+
if (prev.getAttribute && prev.getAttribute('data-ezoic-pin') === '1') break;
|
|
847
|
+
} catch (e) {}
|
|
848
|
+
|
|
849
|
+
// Never remove a wrap that is already filled; otherwise it looks like
|
|
850
|
+
// ads "disappear" while scrolling. Only remove the empty neighbour.
|
|
851
|
+
const prevFilled = isFilled(prev);
|
|
852
|
+
const curFilled = isFilled(w);
|
|
853
|
+
|
|
854
|
+
if (curFilled) {
|
|
855
|
+
// If the previous one is empty (and not fresh), drop the previous instead.
|
|
856
|
+
if (!prevFilled && !isFresh(prev)) {
|
|
857
|
+
withInternalDomChange(() => releaseWrapNode(prev));
|
|
858
|
+
removed++;
|
|
859
|
+
}
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Current is empty.
|
|
864
|
+
// Don't decluster two "fresh" empty wraps — it can reduce fill on slow auctions.
|
|
865
|
+
// Only decluster when previous is filled, or when current is stale.
|
|
866
|
+
if (prevFilled || !isFresh(w)) {
|
|
867
|
+
withInternalDomChange(() => releaseWrapNode(w));
|
|
868
|
+
removed++;
|
|
869
|
+
}
|
|
870
|
+
break;
|
|
871
|
+
}
|
|
872
|
+
prev = prev.previousElementSibling;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return removed;
|
|
876
|
+
}
|
|
877
|
+
|
|
714
878
|
// ---------------- show (preload / fast fill) ----------------
|
|
715
879
|
|
|
716
880
|
function ensurePreloadObserver() {
|
|
@@ -1341,263 +1505,3 @@ function buildOrdinalMap(items) {
|
|
|
1341
1505
|
insertHeroAdEarly().catch(() => {});
|
|
1342
1506
|
requestBurst();
|
|
1343
1507
|
})();
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
// ===== CLEAN REFRACTOR: visibility manager for Ezoic wraps =====
|
|
1353
|
-
(function () {
|
|
1354
|
-
// v2.6 (slot-recycler):
|
|
1355
|
-
// Observed:
|
|
1356
|
-
// - When scrolling up, ad wraps accumulate before first post.
|
|
1357
|
-
// - Ezoic logs: "Placeholder Id X already defined" + "No valid placeholders for loadMore"
|
|
1358
|
-
//
|
|
1359
|
-
// Root cause:
|
|
1360
|
-
// - Infinite scroll / templating introduces MULTIPLE instances of the SAME placeholder id in DOM.
|
|
1361
|
-
// - Ezoic expects each placeholder id to be unique on the page. Duplicates cause warnings and loadMore failure.
|
|
1362
|
-
// - Some scripts then "re-home" the active slot near the top, causing pile-up.
|
|
1363
|
-
//
|
|
1364
|
-
// Fix strategy:
|
|
1365
|
-
// - Keep EXACTLY ONE DOM node per placeholder id ("canonical wrap").
|
|
1366
|
-
// - Replace any duplicate wraps with lightweight SLOT ANCHORS left in place.
|
|
1367
|
-
// - As user scrolls, MOVE the canonical wrap to the closest visible anchor for that id.
|
|
1368
|
-
// (Move, not recreate => no re-define; prevents pile-up; ad follows the viewport.)
|
|
1369
|
-
//
|
|
1370
|
-
// We only handle .nodebb-ezoic-wrap.* nodes; we do not change post markup.
|
|
1371
|
-
|
|
1372
|
-
var WRAP_SELECTOR = '.nodebb-ezoic-wrap';
|
|
1373
|
-
var PLACEHOLDER_SELECTOR = '[data-ezoic-id]';
|
|
1374
|
-
|
|
1375
|
-
// show tuning
|
|
1376
|
-
var MAX_SHOW_PER_TICK = 3;
|
|
1377
|
-
|
|
1378
|
-
// recycler tuning
|
|
1379
|
-
var ROOT_MARGIN = 1400; // how far from viewport we consider an anchor "active"
|
|
1380
|
-
var MOVE_COOLDOWN_MS = 120; // avoid moving too often
|
|
1381
|
-
var SCAN_COOLDOWN_MS = 150; // throttle scroll scans
|
|
1382
|
-
var lastScan = 0;
|
|
1383
|
-
var lastMove = 0;
|
|
1384
|
-
|
|
1385
|
-
// state
|
|
1386
|
-
var canonicalById = Object.create(null); // id -> element
|
|
1387
|
-
var activatedById = Object.create(null); // id -> ts
|
|
1388
|
-
var showQueue = [];
|
|
1389
|
-
var showTicking = false;
|
|
1390
|
-
|
|
1391
|
-
function getId(w) {
|
|
1392
|
-
try {
|
|
1393
|
-
var id = w.getAttribute('data-ezoic-wrapid');
|
|
1394
|
-
if (id) return id;
|
|
1395
|
-
var ph = w.querySelector(PLACEHOLDER_SELECTOR);
|
|
1396
|
-
if (ph) return ph.getAttribute('data-ezoic-id');
|
|
1397
|
-
} catch (e) {}
|
|
1398
|
-
return null;
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
function isFilled(w) {
|
|
1402
|
-
try {
|
|
1403
|
-
if (w.querySelector('iframe')) return true;
|
|
1404
|
-
if (w.querySelector('[id^="google_ads_iframe"]')) return true;
|
|
1405
|
-
if (w.querySelector('.ezoic-ad')) return true;
|
|
1406
|
-
} catch (e) {}
|
|
1407
|
-
return false;
|
|
1408
|
-
}
|
|
1409
|
-
|
|
1410
|
-
function enqueueShow(id) {
|
|
1411
|
-
if (!id) return;
|
|
1412
|
-
if (activatedById[id]) return;
|
|
1413
|
-
for (var i = 0; i < showQueue.length; i++) if (showQueue[i] === id) return;
|
|
1414
|
-
showQueue.push(id);
|
|
1415
|
-
scheduleShowTick();
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
function scheduleShowTick() {
|
|
1419
|
-
if (showTicking) return;
|
|
1420
|
-
showTicking = true;
|
|
1421
|
-
requestAnimationFrame(function () {
|
|
1422
|
-
showTicking = false;
|
|
1423
|
-
var n = 0;
|
|
1424
|
-
while (showQueue.length && n < MAX_SHOW_PER_TICK) {
|
|
1425
|
-
var id = showQueue.shift();
|
|
1426
|
-
try {
|
|
1427
|
-
if (!activatedById[id] && window.ezstandalone && typeof window.ezstandalone.showAds === 'function') {
|
|
1428
|
-
window.ezstandalone.showAds(String(id));
|
|
1429
|
-
activatedById[id] = Date.now();
|
|
1430
|
-
}
|
|
1431
|
-
} catch (e) {}
|
|
1432
|
-
n++;
|
|
1433
|
-
}
|
|
1434
|
-
if (showQueue.length) scheduleShowTick();
|
|
1435
|
-
});
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
function createAnchorForDuplicate(w, id) {
|
|
1439
|
-
try {
|
|
1440
|
-
var a = document.createElement('span');
|
|
1441
|
-
a.className = 'nodebb-ezoic-slot-anchor';
|
|
1442
|
-
a.setAttribute('data-slot-for', String(id));
|
|
1443
|
-
// keep after/index metadata if present
|
|
1444
|
-
var after = w.getAttribute('data-ezoic-after');
|
|
1445
|
-
if (after) a.setAttribute('data-ezoic-after', after);
|
|
1446
|
-
a.style.display = 'block';
|
|
1447
|
-
a.style.width = '100%';
|
|
1448
|
-
// leave minimal height to keep spacing similar without large blank
|
|
1449
|
-
a.style.minHeight = '1px';
|
|
1450
|
-
w.parentNode.insertBefore(a, w);
|
|
1451
|
-
w.remove();
|
|
1452
|
-
} catch (e) {}
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
function normalizeDomOnce() {
|
|
1456
|
-
// Build canonical map; convert duplicates to anchors.
|
|
1457
|
-
try {
|
|
1458
|
-
var wraps = document.querySelectorAll(WRAP_SELECTOR);
|
|
1459
|
-
for (var i = 0; i < wraps.length; i++) {
|
|
1460
|
-
var w = wraps[i];
|
|
1461
|
-
var id = getId(w);
|
|
1462
|
-
if (!id) continue;
|
|
1463
|
-
|
|
1464
|
-
if (!canonicalById[id]) {
|
|
1465
|
-
canonicalById[id] = w;
|
|
1466
|
-
// if already filled, mark activated
|
|
1467
|
-
if (isFilled(w)) activatedById[id] = activatedById[id] || Date.now();
|
|
1468
|
-
} else if (canonicalById[id] !== w) {
|
|
1469
|
-
createAnchorForDuplicate(w, id);
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
} catch (e) {}
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
function getCandidateAnchors(id) {
|
|
1476
|
-
try {
|
|
1477
|
-
return document.querySelectorAll('.nodebb-ezoic-slot-anchor[data-slot-for="' + String(id) + '"]');
|
|
1478
|
-
} catch (e) {
|
|
1479
|
-
return [];
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
|
|
1483
|
-
function anchorDistanceToViewport(a, vh) {
|
|
1484
|
-
try {
|
|
1485
|
-
var r = a.getBoundingClientRect();
|
|
1486
|
-
// distance 0 if overlaps extended viewport, else min distance
|
|
1487
|
-
if (r.bottom >= -ROOT_MARGIN && r.top <= (vh + ROOT_MARGIN)) return 0;
|
|
1488
|
-
if (r.top > (vh + ROOT_MARGIN)) return r.top - (vh + ROOT_MARGIN);
|
|
1489
|
-
if (r.bottom < -ROOT_MARGIN) return (-ROOT_MARGIN) - r.bottom;
|
|
1490
|
-
} catch (e) {}
|
|
1491
|
-
return 1e12;
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
function moveCanonicalToAnchor(id, anchor) {
|
|
1495
|
-
var now = Date.now();
|
|
1496
|
-
if (now - lastMove < MOVE_COOLDOWN_MS) return;
|
|
1497
|
-
lastMove = now;
|
|
1498
|
-
|
|
1499
|
-
try {
|
|
1500
|
-
var w = canonicalById[id];
|
|
1501
|
-
if (!w || !anchor || !anchor.parentNode) return;
|
|
1502
|
-
|
|
1503
|
-
// If already directly after anchor, nothing to do.
|
|
1504
|
-
if (w.parentNode === anchor.parentNode && w.previousSibling === anchor) return;
|
|
1505
|
-
|
|
1506
|
-
// Insert canonical wrap right after anchor.
|
|
1507
|
-
if (anchor.nextSibling) anchor.parentNode.insertBefore(w, anchor.nextSibling);
|
|
1508
|
-
else anchor.parentNode.appendChild(w);
|
|
1509
|
-
} catch (e) {}
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
function recycleTick() {
|
|
1513
|
-
var now = Date.now();
|
|
1514
|
-
if (now - lastScan < SCAN_COOLDOWN_MS) return;
|
|
1515
|
-
lastScan = now;
|
|
1516
|
-
|
|
1517
|
-
// ensure dom normalized before recycling
|
|
1518
|
-
normalizeDomOnce();
|
|
1519
|
-
|
|
1520
|
-
var vh = window.innerHeight || document.documentElement.clientHeight || 0;
|
|
1521
|
-
if (!vh) return;
|
|
1522
|
-
|
|
1523
|
-
try {
|
|
1524
|
-
for (var id in canonicalById) {
|
|
1525
|
-
if (!canonicalById[id]) continue;
|
|
1526
|
-
var anchors = getCandidateAnchors(id);
|
|
1527
|
-
if (!anchors || !anchors.length) continue;
|
|
1528
|
-
|
|
1529
|
-
// choose the first anchor that intersects extended viewport, otherwise closest
|
|
1530
|
-
var best = null;
|
|
1531
|
-
var bestDist = 1e12;
|
|
1532
|
-
for (var i = 0; i < anchors.length; i++) {
|
|
1533
|
-
var a = anchors[i];
|
|
1534
|
-
var d = anchorDistanceToViewport(a, vh);
|
|
1535
|
-
if (d === 0) { best = a; bestDist = 0; break; }
|
|
1536
|
-
if (d < bestDist) { bestDist = d; best = a; }
|
|
1537
|
-
}
|
|
1538
|
-
if (best) {
|
|
1539
|
-
moveCanonicalToAnchor(id, best);
|
|
1540
|
-
|
|
1541
|
-
// trigger show when canonical is near viewport and still empty
|
|
1542
|
-
var w = canonicalById[id];
|
|
1543
|
-
if (w) {
|
|
1544
|
-
try {
|
|
1545
|
-
var r = w.getBoundingClientRect();
|
|
1546
|
-
if (r.bottom >= -ROOT_MARGIN && r.top <= (vh + ROOT_MARGIN)) {
|
|
1547
|
-
if (isFilled(w)) activatedById[id] = activatedById[id] || Date.now();
|
|
1548
|
-
else enqueueShow(id);
|
|
1549
|
-
}
|
|
1550
|
-
} catch (e) {}
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
}
|
|
1554
|
-
} catch (e) {}
|
|
1555
|
-
|
|
1556
|
-
scheduleShowTick();
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
// Observe new content; normalize duplicates immediately
|
|
1560
|
-
var moInstalled = false;
|
|
1561
|
-
function installMO() {
|
|
1562
|
-
if (moInstalled || typeof MutationObserver === 'undefined') return;
|
|
1563
|
-
moInstalled = true;
|
|
1564
|
-
|
|
1565
|
-
var mo = new MutationObserver(function (muts) {
|
|
1566
|
-
// When new nodes arrive, normalize and run one recycle tick.
|
|
1567
|
-
normalizeDomOnce();
|
|
1568
|
-
recycleTick();
|
|
1569
|
-
});
|
|
1570
|
-
|
|
1571
|
-
try { mo.observe(document.documentElement || document.body, { childList: true, subtree: true }); } catch (e) {}
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
function init() {
|
|
1575
|
-
normalizeDomOnce();
|
|
1576
|
-
installMO();
|
|
1577
|
-
|
|
1578
|
-
window.addEventListener('scroll', recycleTick, { passive: true });
|
|
1579
|
-
window.addEventListener('resize', recycleTick, { passive: true });
|
|
1580
|
-
|
|
1581
|
-
if (window.jQuery) {
|
|
1582
|
-
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', function () {
|
|
1583
|
-
setTimeout(function () {
|
|
1584
|
-
normalizeDomOnce();
|
|
1585
|
-
recycleTick();
|
|
1586
|
-
}, 0);
|
|
1587
|
-
});
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
setInterval(recycleTick, 900);
|
|
1591
|
-
setTimeout(recycleTick, 0);
|
|
1592
|
-
}
|
|
1593
|
-
|
|
1594
|
-
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
|
1595
|
-
else init();
|
|
1596
|
-
})();
|
|
1597
|
-
// ===== /CLEAN REFRACTOR =====
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
package/public/style.css
CHANGED
|
@@ -81,7 +81,9 @@
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
/* =====
|
|
85
|
-
.nodebb-ezoic-
|
|
86
|
-
/*
|
|
84
|
+
/* ===== V12.1 li host wrapper ===== */
|
|
85
|
+
li.nodebb-ezoic-host { list-style: none; width: 100%; }
|
|
86
|
+
/* keep inner wrap full width */
|
|
87
|
+
li.nodebb-ezoic-host > .nodebb-ezoic-wrap { width: 100%; }
|
|
88
|
+
/* ===== /V12.1 ===== */
|
|
87
89
|
|