nodebb-plugin-ezoic-infinite 1.5.54 → 1.5.56
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 +112 -3
- package/public/style.css +2 -0
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -7,6 +7,89 @@
|
|
|
7
7
|
const WRAP_CLASS = 'ezoic-ad';
|
|
8
8
|
const PLACEHOLDER_PREFIX = 'ezoic-pub-ad-placeholder-';
|
|
9
9
|
|
|
10
|
+
|
|
11
|
+
// Offscreen pool to keep placeholder elements alive across ajaxify/navigation.
|
|
12
|
+
// This prevents Ezoic from trying to define ids that are not currently injected,
|
|
13
|
+
// and eliminates "HTML element with id ... does not exist" noise.
|
|
14
|
+
const POOL_ID = 'ezoic-placeholder-pool';
|
|
15
|
+
|
|
16
|
+
function ensurePool() {
|
|
17
|
+
let pool = document.getElementById(POOL_ID);
|
|
18
|
+
if (pool) return pool;
|
|
19
|
+
pool = document.createElement('div');
|
|
20
|
+
pool.id = POOL_ID;
|
|
21
|
+
pool.style.position = 'absolute';
|
|
22
|
+
pool.style.left = '-99999px';
|
|
23
|
+
pool.style.top = '0';
|
|
24
|
+
pool.style.width = '1px';
|
|
25
|
+
pool.style.height = '1px';
|
|
26
|
+
pool.style.overflow = 'hidden';
|
|
27
|
+
pool.setAttribute('aria-hidden', 'true');
|
|
28
|
+
// Attach early (documentElement exists before body), so placeholders are always connected.
|
|
29
|
+
try { (document.documentElement || document.body).appendChild(pool); } catch (e) {}
|
|
30
|
+
// If body exists later, move it under body (purely cosmetic).
|
|
31
|
+
try {
|
|
32
|
+
if (document.body && pool.parentNode !== document.body) {
|
|
33
|
+
document.body.appendChild(pool);
|
|
34
|
+
}
|
|
35
|
+
} catch (e) {}
|
|
36
|
+
return pool;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Create placeholder divs for all configured ids upfront.
|
|
40
|
+
// Ezoic sometimes attempts to initialize/refresh a range of ids even if we
|
|
41
|
+
// haven't injected them yet; keeping them in the offscreen pool prevents
|
|
42
|
+
// "HTML element ... does not exist" spam.
|
|
43
|
+
function primePool(ids) {
|
|
44
|
+
try {
|
|
45
|
+
if (!ids || !ids.length) return;
|
|
46
|
+
const pool = ensurePool();
|
|
47
|
+
for (const v of ids) {
|
|
48
|
+
const id = parseInt(v, 10);
|
|
49
|
+
if (!Number.isFinite(id) || id <= 0) continue;
|
|
50
|
+
const domId = `${PLACEHOLDER_PREFIX}${id}`;
|
|
51
|
+
let ph = document.getElementById(domId);
|
|
52
|
+
if (!ph) {
|
|
53
|
+
ph = document.createElement('div');
|
|
54
|
+
ph.id = domId;
|
|
55
|
+
ph.setAttribute('data-ezoic-id', String(id));
|
|
56
|
+
pool.appendChild(ph);
|
|
57
|
+
} else if (!ph.isConnected) {
|
|
58
|
+
pool.appendChild(ph);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch (e) {}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function acquirePlaceholder(id) {
|
|
65
|
+
const domId = `${PLACEHOLDER_PREFIX}${id}`;
|
|
66
|
+
let ph = document.getElementById(domId);
|
|
67
|
+
if (!ph) {
|
|
68
|
+
ph = document.createElement('div');
|
|
69
|
+
ph.id = domId;
|
|
70
|
+
ph.setAttribute('data-ezoic-id', String(id));
|
|
71
|
+
ensurePool().appendChild(ph);
|
|
72
|
+
}
|
|
73
|
+
// Detach from wherever it currently is (pool or a previous wrap)
|
|
74
|
+
try { if (ph.parentNode) ph.parentNode.removeChild(ph); } catch (e) {}
|
|
75
|
+
// Clear request/defined flags when reusing
|
|
76
|
+
try {
|
|
77
|
+
if (ph.dataset) {
|
|
78
|
+
ph.dataset.ezRequested = '0';
|
|
79
|
+
ph.dataset.ezDefined = '0';
|
|
80
|
+
}
|
|
81
|
+
} catch (e) {}
|
|
82
|
+
return ph;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function parkPlaceholderFromWrap(wrap) {
|
|
86
|
+
try {
|
|
87
|
+
const ph = wrap && wrap.querySelector ? wrap.querySelector(`[id^="${PLACEHOLDER_PREFIX}"]`) : null;
|
|
88
|
+
if (!ph) return;
|
|
89
|
+
try { if (state && state.io) state.io.unobserve(ph); } catch (e) {}
|
|
90
|
+
ensurePool().appendChild(ph);
|
|
91
|
+
} catch (e) {}
|
|
92
|
+
}
|
|
10
93
|
// Insert at most N ads per run to keep the UI smooth on infinite scroll
|
|
11
94
|
const MAX_INSERTS_PER_RUN = 3;
|
|
12
95
|
|
|
@@ -358,6 +441,12 @@ function withInternalDomChange(fn) {
|
|
|
358
441
|
if (state.allTopics.length === 0) state.allTopics = parsePool(cfg.placeholderIds);
|
|
359
442
|
if (state.allPosts.length === 0) state.allPosts = parsePool(cfg.messagePlaceholderIds);
|
|
360
443
|
if (state.allCategories.length === 0) state.allCategories = parsePool(cfg.categoryPlaceholderIds);
|
|
444
|
+
|
|
445
|
+
// Keep placeholders alive even before they're injected.
|
|
446
|
+
// This avoids Ezoic trying to touch ids that we haven't inserted yet.
|
|
447
|
+
primePool(state.allTopics);
|
|
448
|
+
primePool(state.allPosts);
|
|
449
|
+
primePool(state.allCategories);
|
|
361
450
|
}
|
|
362
451
|
|
|
363
452
|
// ---------- insertion primitives ----------
|
|
@@ -422,6 +511,7 @@ function withInternalDomChange(fn) {
|
|
|
422
511
|
withInternalDomChange(() => {
|
|
423
512
|
try {
|
|
424
513
|
if (id) safeDestroyById(id);
|
|
514
|
+
parkPlaceholderFromWrap(wrap);
|
|
425
515
|
wrap.remove();
|
|
426
516
|
} catch (e) {}
|
|
427
517
|
});
|
|
@@ -455,9 +545,7 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
455
545
|
wrap.setAttribute('data-ezoic-wrapid', String(id));
|
|
456
546
|
wrap.style.width = '100%';
|
|
457
547
|
|
|
458
|
-
const ph =
|
|
459
|
-
ph.id = `${PLACEHOLDER_PREFIX}${id}`;
|
|
460
|
-
ph.setAttribute('data-ezoic-id', String(id));
|
|
548
|
+
const ph = acquirePlaceholder(id);
|
|
461
549
|
wrap.appendChild(ph);
|
|
462
550
|
|
|
463
551
|
return wrap;
|
|
@@ -524,6 +612,7 @@ function buildWrap(id, kindClass, afterPos) {
|
|
|
524
612
|
if (ph && state.io) state.io.unobserve(ph);
|
|
525
613
|
} catch (e) {}
|
|
526
614
|
|
|
615
|
+
parkPlaceholderFromWrap(victim);
|
|
527
616
|
victim.remove();
|
|
528
617
|
return true;
|
|
529
618
|
} catch (e) {
|
|
@@ -735,6 +824,25 @@ function startShow(id) {
|
|
|
735
824
|
inserted += 1;
|
|
736
825
|
}
|
|
737
826
|
|
|
827
|
+
// Safety: if DOM churn results in two consecutive ad wrappers, remove the latter.
|
|
828
|
+
// (This can happen when NodeBB inserts/removes spacer elements around the anchor.)
|
|
829
|
+
try {
|
|
830
|
+
const wraps = Array.from(document.querySelectorAll(`.${WRAP_CLASS}.${kindClass}`));
|
|
831
|
+
for (const w of wraps) {
|
|
832
|
+
const prev = w.previousElementSibling;
|
|
833
|
+
if (prev && prev.classList && prev.classList.contains(WRAP_CLASS)) {
|
|
834
|
+
withInternalDomChange(() => {
|
|
835
|
+
try {
|
|
836
|
+
const id = getWrapIdFromWrap(w);
|
|
837
|
+
if (id) safeDestroyById(id);
|
|
838
|
+
parkPlaceholderFromWrap(w);
|
|
839
|
+
w.remove();
|
|
840
|
+
} catch (e) {}
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
} catch (e) {}
|
|
845
|
+
|
|
738
846
|
return inserted;
|
|
739
847
|
}
|
|
740
848
|
|
|
@@ -870,6 +978,7 @@ function startShow(id) {
|
|
|
870
978
|
// remove all wrappers
|
|
871
979
|
try {
|
|
872
980
|
document.querySelectorAll(`.${WRAP_CLASS}`).forEach((el) => {
|
|
981
|
+
try { parkPlaceholderFromWrap(el); } catch (e) {}
|
|
873
982
|
try { el.remove(); } catch (e) {}
|
|
874
983
|
});
|
|
875
984
|
} catch (e) {}
|