nodebb-plugin-ezoic-infinite 1.2.3 → 1.2.6
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 +84 -2
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -93,11 +93,57 @@
|
|
|
93
93
|
return 'category';
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
|
|
97
|
+
function getTopicItems() {
|
|
98
|
+
return Array.from(document.querySelectorAll(SELECTORS.topicItem));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getPostContainers() {
|
|
102
|
+
// Harmony can include multiple [component="post"] blocks (e.g. parent previews, nested structures).
|
|
103
|
+
// We only want top-level post containers that actually contain post content.
|
|
104
|
+
const nodes = Array.from(document.querySelectorAll(SELECTORS.postItem));
|
|
105
|
+
return nodes.filter((el) => {
|
|
106
|
+
if (!el || !el.isConnected) return false;
|
|
107
|
+
|
|
108
|
+
// Must contain post content
|
|
109
|
+
if (!el.querySelector('[component="post/content"]')) return false;
|
|
110
|
+
|
|
111
|
+
// Must not be nested within another post container
|
|
112
|
+
const parentPost = el.parentElement && el.parentElement.closest('[component="post"][data-pid]');
|
|
113
|
+
if (parentPost && parentPost !== el) return false;
|
|
114
|
+
|
|
115
|
+
// Avoid "parent/quote" blocks that use component="post/parent"
|
|
116
|
+
if (el.getAttribute('component') === 'post/parent') return false;
|
|
117
|
+
|
|
118
|
+
return true;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
96
122
|
function hasAdImmediatelyAfter(el) {
|
|
97
123
|
const n = el && el.nextElementSibling;
|
|
98
124
|
return !!(n && n.classList && n.classList.contains(WRAP_CLASS));
|
|
99
125
|
}
|
|
100
126
|
|
|
127
|
+
function enforceNoAdjacentAds() {
|
|
128
|
+
// NodeBB can virtualize (remove) topics/posts from the DOM while keeping our ad wrappers,
|
|
129
|
+
// which can temporarily make two ad wrappers adjacent. Hide the later one to avoid back-to-back ads.
|
|
130
|
+
const ads = Array.from(document.querySelectorAll(`.${WRAP_CLASS}`));
|
|
131
|
+
for (let i = 0; i < ads.length; i++) {
|
|
132
|
+
const ad = ads[i];
|
|
133
|
+
const prev = ad.previousElementSibling;
|
|
134
|
+
if (prev && prev.classList && prev.classList.contains(WRAP_CLASS)) {
|
|
135
|
+
ad.style.display = 'none';
|
|
136
|
+
} else {
|
|
137
|
+
ad.style.display = '';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
const n = el && el.nextElementSibling;
|
|
144
|
+
return !!(n && n.classList && n.classList.contains(WRAP_CLASS));
|
|
145
|
+
}
|
|
146
|
+
|
|
101
147
|
function buildWrap(id, kind, afterPos) {
|
|
102
148
|
const wrap = document.createElement('div');
|
|
103
149
|
wrap.className = `${WRAP_CLASS} ${kind}`;
|
|
@@ -248,7 +294,7 @@
|
|
|
248
294
|
const interval = Math.max(1, parseInt(cfg.intervalPosts, 10) || 6);
|
|
249
295
|
const first = normalizeBool(cfg.showFirstTopicAd);
|
|
250
296
|
|
|
251
|
-
const items =
|
|
297
|
+
const items = getTopicItems();
|
|
252
298
|
if (!items.length) return 0;
|
|
253
299
|
|
|
254
300
|
let inserted = 0;
|
|
@@ -288,7 +334,7 @@
|
|
|
288
334
|
const interval = Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3);
|
|
289
335
|
const first = normalizeBool(cfg.showFirstMessageAd);
|
|
290
336
|
|
|
291
|
-
const posts =
|
|
337
|
+
const posts = getPostContainers();
|
|
292
338
|
if (!posts.length) return 0;
|
|
293
339
|
|
|
294
340
|
let inserted = 0;
|
|
@@ -321,7 +367,40 @@
|
|
|
321
367
|
return inserted;
|
|
322
368
|
}
|
|
323
369
|
|
|
370
|
+
|
|
371
|
+
function destroyUsedPlaceholders() {
|
|
372
|
+
const ids = [];
|
|
373
|
+
try {
|
|
374
|
+
state.usedTopics.forEach((id) => ids.push(id));
|
|
375
|
+
state.usedPosts.forEach((id) => ids.push(id));
|
|
376
|
+
} catch (e) {}
|
|
377
|
+
|
|
378
|
+
if (!ids.length) return;
|
|
379
|
+
|
|
380
|
+
const call = () => {
|
|
381
|
+
try {
|
|
382
|
+
if (window.ezstandalone && typeof window.ezstandalone.destroyPlaceholders === 'function') {
|
|
383
|
+
window.ezstandalone.destroyPlaceholders(ids);
|
|
384
|
+
}
|
|
385
|
+
} catch (e) {}
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
window.ezstandalone = window.ezstandalone || {};
|
|
390
|
+
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
391
|
+
|
|
392
|
+
if (window.ezstandalone && typeof window.ezstandalone.destroyPlaceholders === 'function') {
|
|
393
|
+
call();
|
|
394
|
+
} else {
|
|
395
|
+
// queue for when ezstandalone becomes ready
|
|
396
|
+
window.ezstandalone.cmd.push(call);
|
|
397
|
+
}
|
|
398
|
+
} catch (e) {}
|
|
399
|
+
}
|
|
400
|
+
|
|
324
401
|
function cleanup() {
|
|
402
|
+
// Destroy slots for IDs we used on the previous view before we reuse the same IDs on the next page
|
|
403
|
+
destroyUsedPlaceholders();
|
|
325
404
|
state.pageKey = getPageKey();
|
|
326
405
|
state.cfg = null;
|
|
327
406
|
state.cfgPromise = null;
|
|
@@ -354,6 +433,7 @@
|
|
|
354
433
|
|
|
355
434
|
async function runCore() {
|
|
356
435
|
patchShowAds();
|
|
436
|
+
patchShowAds();
|
|
357
437
|
|
|
358
438
|
const cfg = await fetchConfig();
|
|
359
439
|
if (!cfg || cfg.excluded) return;
|
|
@@ -366,6 +446,8 @@
|
|
|
366
446
|
if (kind === 'topic') inserted = injectPosts(cfg);
|
|
367
447
|
else inserted = injectTopics(cfg);
|
|
368
448
|
|
|
449
|
+
enforceNoAdjacentAds();
|
|
450
|
+
|
|
369
451
|
// If we inserted max per run, schedule another pass to gradually fill (avoids “burst”)
|
|
370
452
|
if (inserted >= MAX_INSERTS_PER_RUN) {
|
|
371
453
|
setTimeout(() => scheduleRun('continue-fill'), 120);
|