nodebb-plugin-ezoic-infinite 1.5.65 → 1.5.67
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 +70 -119
- package/public/style.css +4 -4
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -95,20 +95,11 @@
|
|
|
95
95
|
|
|
96
96
|
// hero)
|
|
97
97
|
heroDoneForPage: false,
|
|
98
|
+
burstRuns: 0,
|
|
98
99
|
};
|
|
99
100
|
|
|
100
101
|
const insertingIds = new Set();
|
|
101
102
|
|
|
102
|
-
function unemptyIfFilled(wrap, ph) {
|
|
103
|
-
try {
|
|
104
|
-
if (!wrap || !ph) return false;
|
|
105
|
-
const hasAd = !!(ph.querySelector && ph.querySelector('iframe, ins, img, .ez-ad, .ezoic-ad'));
|
|
106
|
-
if (hasAd) wrap.classList.remove('is-empty');
|
|
107
|
-
return hasAd;
|
|
108
|
-
} catch (e) { return false; }
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
103
|
|
|
113
104
|
function markEmptyWrapper(id) {
|
|
114
105
|
try {
|
|
@@ -126,17 +117,6 @@
|
|
|
126
117
|
// consider empty if only whitespace and no iframes/ins/img
|
|
127
118
|
const hasAd = !!(ph2.querySelector && ph2.querySelector('iframe, ins, img, .ez-ad, .ezoic-ad'));
|
|
128
119
|
if (!hasAd) w2.classList.add('is-empty');
|
|
129
|
-
// If the ad fills later, immediately uncollapse to avoid "missing ads" perception.
|
|
130
|
-
if (!unemptyIfFilled(w2, ph2)) {
|
|
131
|
-
try {
|
|
132
|
-
const mo = new MutationObserver(() => {
|
|
133
|
-
if (unemptyIfFilled(w2, ph2)) { try { mo.disconnect(); } catch (e) {} }
|
|
134
|
-
});
|
|
135
|
-
mo.observe(ph2, { childList: true, subtree: true });
|
|
136
|
-
// safety stop
|
|
137
|
-
setTimeout(() => { try { mo.disconnect(); } catch (e) {} }, 30000);
|
|
138
|
-
} catch (e) {}
|
|
139
|
-
}
|
|
140
120
|
} catch (e) {}
|
|
141
121
|
}, 3500);
|
|
142
122
|
} catch (e) {}
|
|
@@ -255,40 +235,22 @@
|
|
|
255
235
|
window.__nodebbEzoicPatched = true;
|
|
256
236
|
const orig = ez.showAds;
|
|
257
237
|
|
|
258
|
-
// Important: preserve the original calling convention.
|
|
259
|
-
// Some Ezoic builds expect an array; calling one-by-one can lead to
|
|
260
|
-
// repeated define attempts and "Placeholder Id ... already been defined".
|
|
261
238
|
ez.showAds = function (...args) {
|
|
262
239
|
if (isBlocked()) return;
|
|
263
240
|
|
|
264
|
-
const now = Date.now();
|
|
265
241
|
let ids = [];
|
|
266
|
-
|
|
267
|
-
if (isArrayCall) ids = args[0];
|
|
242
|
+
if (args.length === 1 && Array.isArray(args[0])) ids = args[0];
|
|
268
243
|
else ids = args;
|
|
269
244
|
|
|
270
|
-
const filtered = [];
|
|
271
245
|
const seen = new Set();
|
|
272
246
|
for (const v of ids) {
|
|
273
247
|
const id = parseInt(v, 10);
|
|
274
248
|
if (!Number.isFinite(id) || id <= 0 || seen.has(id)) continue;
|
|
275
249
|
const ph = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
276
250
|
if (!ph || !ph.isConnected) continue;
|
|
277
|
-
|
|
278
|
-
// Extra throttle to avoid rapid duplicate defines during ajaxify churn
|
|
279
|
-
const last = state.lastShowById.get(id) || 0;
|
|
280
|
-
if (now - last < 650) continue;
|
|
281
|
-
state.lastShowById.set(id, now);
|
|
282
|
-
|
|
283
251
|
seen.add(id);
|
|
284
|
-
|
|
252
|
+
try { orig.call(ez, id); } catch (e) {}
|
|
285
253
|
}
|
|
286
|
-
|
|
287
|
-
if (!filtered.length) return;
|
|
288
|
-
try {
|
|
289
|
-
if (isArrayCall) orig.call(ez, filtered);
|
|
290
|
-
else orig.apply(ez, filtered);
|
|
291
|
-
} catch (e) {}
|
|
292
254
|
};
|
|
293
255
|
} catch (e) {}
|
|
294
256
|
};
|
|
@@ -386,21 +348,11 @@ function withInternalDomChange(fn) {
|
|
|
386
348
|
// NodeBB can insert separators/spacers; accept an anchor within a few previous siblings
|
|
387
349
|
let ok = false;
|
|
388
350
|
let prev = wrap.previousElementSibling;
|
|
389
|
-
for (let i = 0; i <
|
|
351
|
+
for (let i = 0; i < 3 && prev; i++) {
|
|
390
352
|
if (itemSet.has(prev)) { ok = true; break; }
|
|
391
353
|
prev = prev.previousElementSibling;
|
|
392
354
|
}
|
|
393
355
|
|
|
394
|
-
// If it is already filled (iframe/ins/img), be conservative and keep it.
|
|
395
|
-
// Prevents ads "disappearing too fast" during ajaxify churn / minor rerenders.
|
|
396
|
-
if (!ok) {
|
|
397
|
-
try {
|
|
398
|
-
const ph = wrap.querySelector && wrap.querySelector(`[id^="${PLACEHOLDER_PREFIX}"]`);
|
|
399
|
-
const filled = !!(ph && ph.querySelector && ph.querySelector('iframe, ins, img, .ez-ad, .ezoic-ad'));
|
|
400
|
-
if (filled) ok = true;
|
|
401
|
-
} catch (e) {}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
356
|
if (!ok) {
|
|
405
357
|
const id = getWrapIdFromWrap(wrap);
|
|
406
358
|
withInternalDomChange(() => {
|
|
@@ -417,28 +369,6 @@ function withInternalDomChange(fn) {
|
|
|
417
369
|
return removed;
|
|
418
370
|
}
|
|
419
371
|
|
|
420
|
-
function declusterWraps(kindClass) {
|
|
421
|
-
try {
|
|
422
|
-
const wraps = Array.from(document.querySelectorAll(`.${WRAP_CLASS}.${kindClass}`));
|
|
423
|
-
if (wraps.length < 2) return;
|
|
424
|
-
for (let i = 1; i < wraps.length; i++) {
|
|
425
|
-
const w = wraps[i];
|
|
426
|
-
if (!w || !w.isConnected) continue;
|
|
427
|
-
// If previous siblings contain another wrap within 2 hops, remove this one.
|
|
428
|
-
let prev = w.previousElementSibling;
|
|
429
|
-
let hops = 0;
|
|
430
|
-
while (prev && hops < 3) {
|
|
431
|
-
if (prev.classList && prev.classList.contains(WRAP_CLASS)) {
|
|
432
|
-
withInternalDomChange(() => { try { w.remove(); } catch (e) {} });
|
|
433
|
-
break;
|
|
434
|
-
}
|
|
435
|
-
prev = prev.previousElementSibling;
|
|
436
|
-
hops++;
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
} catch (e) {}
|
|
440
|
-
}
|
|
441
|
-
|
|
442
372
|
function refreshEmptyState(id) {
|
|
443
373
|
// After Ezoic has had a moment to fill the placeholder, toggle the CSS class.
|
|
444
374
|
window.setTimeout(() => {
|
|
@@ -454,25 +384,17 @@ function withInternalDomChange(fn) {
|
|
|
454
384
|
}, 3500);
|
|
455
385
|
}
|
|
456
386
|
|
|
457
|
-
function buildWrap(id, kindClass, afterPos
|
|
387
|
+
function buildWrap(id, kindClass, afterPos) {
|
|
458
388
|
const wrap = document.createElement('div');
|
|
459
389
|
wrap.className = `${WRAP_CLASS} ${kindClass}`;
|
|
460
390
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
461
391
|
wrap.setAttribute('data-ezoic-wrapid', String(id));
|
|
462
392
|
wrap.style.width = '100%';
|
|
463
393
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
} catch (e) {}
|
|
469
|
-
wrap.appendChild(existingPlaceholder);
|
|
470
|
-
} else {
|
|
471
|
-
const ph = document.createElement('div');
|
|
472
|
-
ph.id = `${PLACEHOLDER_PREFIX}${id}`;
|
|
473
|
-
ph.setAttribute('data-ezoic-id', String(id));
|
|
474
|
-
wrap.appendChild(ph);
|
|
475
|
-
}
|
|
394
|
+
const ph = document.createElement('div');
|
|
395
|
+
ph.id = `${PLACEHOLDER_PREFIX}${id}`;
|
|
396
|
+
ph.setAttribute('data-ezoic-id', String(id));
|
|
397
|
+
wrap.appendChild(ph);
|
|
476
398
|
|
|
477
399
|
return wrap;
|
|
478
400
|
}
|
|
@@ -486,28 +408,24 @@ function buildWrap(id, kindClass, afterPos, existingPlaceholder) {
|
|
|
486
408
|
if (findWrap(kindClass, afterPos)) return null;
|
|
487
409
|
if (insertingIds.has(id)) return null;
|
|
488
410
|
|
|
411
|
+
const existingPh = document.getElementById(`${PLACEHOLDER_PREFIX}${id}`);
|
|
412
|
+
|
|
489
413
|
insertingIds.add(id);
|
|
490
414
|
try {
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
//
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
if (existingPh && existingPh.isConnected) {
|
|
499
|
-
moved = existingPh;
|
|
500
|
-
// If it was inside one of our wrappers, drop that empty wrapper.
|
|
415
|
+
const wrap = buildWrap(id, kindClass, afterPos);
|
|
416
|
+
target.insertAdjacentElement('afterend', wrap);
|
|
417
|
+
|
|
418
|
+
// If a placeholder with this id already exists elsewhere (some Ezoic flows
|
|
419
|
+
// pre-create placeholders), move it into our wrapper instead of aborting.
|
|
420
|
+
// replaceChild moves the node atomically (no detach window).
|
|
421
|
+
if (existingPh && existingPh !== wrap.firstElementChild) {
|
|
501
422
|
try {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
}
|
|
423
|
+
existingPh.setAttribute('data-ezoic-id', String(id));
|
|
424
|
+
wrap.replaceChild(existingPh, wrap.firstElementChild);
|
|
425
|
+
} catch (e) {
|
|
426
|
+
// Keep the new placeholder if replace fails.
|
|
427
|
+
}
|
|
507
428
|
}
|
|
508
|
-
|
|
509
|
-
const wrap = buildWrap(id, kindClass, afterPos, moved);
|
|
510
|
-
target.insertAdjacentElement('afterend', wrap);
|
|
511
429
|
return wrap;
|
|
512
430
|
} finally {
|
|
513
431
|
insertingIds.delete(id);
|
|
@@ -707,6 +625,15 @@ function startShow(id) {
|
|
|
707
625
|
if (i % interval === 0) out.push(i);
|
|
708
626
|
}
|
|
709
627
|
return Array.from(new Set(out)).sort((a, b) => a - b);
|
|
628
|
+
// If we inserted the maximum batch, likely there are more targets.
|
|
629
|
+
// Schedule a follow-up pass (bounded via scheduleRun coalescing).
|
|
630
|
+
const maxInserts = MAX_INSERTS_PER_RUN + (isBoosted() ? 1 : 0);
|
|
631
|
+
if (insertedThisRun >= maxInserts) {
|
|
632
|
+
scheduleRun(120);
|
|
633
|
+
scheduleRun(420);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
710
637
|
}
|
|
711
638
|
|
|
712
639
|
function injectBetween(kindClass, items, interval, showFirst, allIds, cursorKey) {
|
|
@@ -813,6 +740,7 @@ function startShow(id) {
|
|
|
813
740
|
|
|
814
741
|
async function runCore() {
|
|
815
742
|
if (isBlocked()) { dbg('blocked'); return; }
|
|
743
|
+
let insertedThisRun = 0;
|
|
816
744
|
|
|
817
745
|
patchShowAds();
|
|
818
746
|
|
|
@@ -827,7 +755,7 @@ function startShow(id) {
|
|
|
827
755
|
if (normalizeBool(cfg.enableMessageAds)) {
|
|
828
756
|
const __items = getPostContainers();
|
|
829
757
|
pruneOrphanWraps('ezoic-ad-message', __items);
|
|
830
|
-
injectBetween(
|
|
758
|
+
insertedThisRun += injectBetween(
|
|
831
759
|
'ezoic-ad-message',
|
|
832
760
|
__items,
|
|
833
761
|
Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3),
|
|
@@ -835,13 +763,12 @@ function startShow(id) {
|
|
|
835
763
|
state.allPosts,
|
|
836
764
|
'curPosts'
|
|
837
765
|
);
|
|
838
|
-
declusterWraps('ezoic-ad-message');
|
|
839
766
|
}
|
|
840
767
|
} else if (kind === 'categoryTopics') {
|
|
841
768
|
if (normalizeBool(cfg.enableBetweenAds)) {
|
|
842
769
|
const __items = getTopicItems();
|
|
843
770
|
pruneOrphanWraps('ezoic-ad-between', __items);
|
|
844
|
-
injectBetween(
|
|
771
|
+
insertedThisRun += injectBetween(
|
|
845
772
|
'ezoic-ad-between',
|
|
846
773
|
__items,
|
|
847
774
|
Math.max(1, parseInt(cfg.intervalPosts, 10) || 6),
|
|
@@ -849,13 +776,12 @@ function startShow(id) {
|
|
|
849
776
|
state.allTopics,
|
|
850
777
|
'curTopics'
|
|
851
778
|
);
|
|
852
|
-
declusterWraps('ezoic-ad-between');
|
|
853
779
|
}
|
|
854
780
|
} else if (kind === 'categories') {
|
|
855
781
|
if (normalizeBool(cfg.enableCategoryAds)) {
|
|
856
782
|
const __items = getCategoryItems();
|
|
857
783
|
pruneOrphanWraps('ezoic-ad-categories', __items);
|
|
858
|
-
injectBetween(
|
|
784
|
+
insertedThisRun += injectBetween(
|
|
859
785
|
'ezoic-ad-categories',
|
|
860
786
|
__items,
|
|
861
787
|
Math.max(1, parseInt(cfg.intervalCategories, 10) || 4),
|
|
@@ -863,22 +789,47 @@ function startShow(id) {
|
|
|
863
789
|
state.allCategories,
|
|
864
790
|
'curCategories'
|
|
865
791
|
);
|
|
866
|
-
declusterWraps('ezoic-ad-categories');
|
|
867
792
|
}
|
|
868
793
|
}
|
|
869
794
|
}
|
|
870
795
|
|
|
871
|
-
function scheduleRun() {
|
|
796
|
+
function scheduleRun(delayMs = 0) {
|
|
797
|
+
// schedule a single run (coalesced)
|
|
872
798
|
if (state.runQueued) return;
|
|
873
799
|
state.runQueued = true;
|
|
874
|
-
|
|
800
|
+
const doRun = () => {
|
|
875
801
|
state.runQueued = false;
|
|
876
802
|
const pk = getPageKey();
|
|
877
803
|
if (state.pageKey && pk !== state.pageKey) return;
|
|
878
804
|
runCore().catch(() => {});
|
|
879
|
-
}
|
|
805
|
+
};
|
|
806
|
+
if (delayMs > 0) {
|
|
807
|
+
window.setTimeout(() => window.requestAnimationFrame(doRun), delayMs);
|
|
808
|
+
} else {
|
|
809
|
+
window.requestAnimationFrame(doRun);
|
|
810
|
+
}
|
|
880
811
|
}
|
|
881
812
|
|
|
813
|
+
function scheduleBurst() {
|
|
814
|
+
// During ajaxify/infinite scroll, the DOM may arrive in waves.
|
|
815
|
+
// We run a small, bounded burst to ensure all 1/X targets are reached.
|
|
816
|
+
const pk = getPageKey();
|
|
817
|
+
state.pageKey = pk;
|
|
818
|
+
state.burstRuns = 0;
|
|
819
|
+
const burst = () => {
|
|
820
|
+
if (getPageKey() !== pk) return;
|
|
821
|
+
if (state.burstRuns >= 6) return;
|
|
822
|
+
state.burstRuns += 1;
|
|
823
|
+
scheduleRun(0);
|
|
824
|
+
// follow-up passes catch late-rendered items (especially on topics)
|
|
825
|
+
window.setTimeout(() => scheduleRun(0), 180);
|
|
826
|
+
window.setTimeout(() => scheduleRun(0), 650);
|
|
827
|
+
window.setTimeout(() => scheduleRun(0), 1400);
|
|
828
|
+
};
|
|
829
|
+
burst();
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
|
|
882
833
|
// ---------- observers / lifecycle ----------
|
|
883
834
|
|
|
884
835
|
function cleanup() {
|
|
@@ -913,7 +864,7 @@ function startShow(id) {
|
|
|
913
864
|
if (state.domObs) return;
|
|
914
865
|
state.domObs = new MutationObserver(() => {
|
|
915
866
|
if (state.internalDomChange > 0) return;
|
|
916
|
-
if (!isBlocked())
|
|
867
|
+
if (!isBlocked()) scheduleBurst();
|
|
917
868
|
});
|
|
918
869
|
try {
|
|
919
870
|
state.domObs.observe(document.body, { childList: true, subtree: true });
|
|
@@ -942,13 +893,13 @@ function startShow(id) {
|
|
|
942
893
|
insertHeroAdEarly().catch(() => {});
|
|
943
894
|
|
|
944
895
|
// Then normal insertion
|
|
945
|
-
|
|
896
|
+
scheduleBurst();
|
|
946
897
|
});
|
|
947
898
|
|
|
948
899
|
// Infinite scroll / partial updates
|
|
949
900
|
$(window).on('action:posts.loaded.ezoicInfinite action:topics.loaded.ezoicInfinite action:category.loaded.ezoicInfinite action:topic.loaded.ezoicInfinite', () => {
|
|
950
901
|
if (isBlocked()) return;
|
|
951
|
-
|
|
902
|
+
scheduleBurst();
|
|
952
903
|
});
|
|
953
904
|
}
|
|
954
905
|
|
|
@@ -982,7 +933,7 @@ function startShow(id) {
|
|
|
982
933
|
ticking = true;
|
|
983
934
|
window.requestAnimationFrame(() => {
|
|
984
935
|
ticking = false;
|
|
985
|
-
if (!isBlocked())
|
|
936
|
+
if (!isBlocked()) scheduleBurst();
|
|
986
937
|
});
|
|
987
938
|
}, { passive: true });
|
|
988
939
|
}
|
|
@@ -1001,5 +952,5 @@ function startShow(id) {
|
|
|
1001
952
|
// First paint: try hero + run
|
|
1002
953
|
blockedUntil = 0;
|
|
1003
954
|
insertHeroAdEarly().catch(() => {});
|
|
1004
|
-
|
|
955
|
+
scheduleBurst();
|
|
1005
956
|
})();
|
package/public/style.css
CHANGED
|
@@ -29,17 +29,17 @@
|
|
|
29
29
|
display: block !important;
|
|
30
30
|
margin: 0 !important;
|
|
31
31
|
padding: 0 !important;
|
|
32
|
-
height:
|
|
33
|
-
min-height:
|
|
32
|
+
height: 0 !important;
|
|
33
|
+
min-height: 0 !important;
|
|
34
34
|
overflow: hidden !important;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
.nodebb-ezoic-wrap {
|
|
38
|
-
min-height:
|
|
38
|
+
min-height: 0 !important;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
.nodebb-ezoic-wrap > [id^="ezoic-pub-ad-placeholder-"] {
|
|
42
|
-
min-height:
|
|
42
|
+
min-height: 0 !important;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/*
|