nodebb-plugin-ezoic-infinite 1.2.2 → 1.2.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 +52 -46
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -35,10 +35,6 @@
|
|
|
35
35
|
seenTopicAnchors: new WeakSet(),
|
|
36
36
|
seenPostAnchors: new WeakSet(),
|
|
37
37
|
|
|
38
|
-
// FIFO for recycling
|
|
39
|
-
fifoTopics: [],
|
|
40
|
-
fifoPosts: [],
|
|
41
|
-
|
|
42
38
|
// showAds anti-double
|
|
43
39
|
lastShowById: new Map(),
|
|
44
40
|
pendingById: new Set(),
|
|
@@ -97,11 +93,57 @@
|
|
|
97
93
|
return 'category';
|
|
98
94
|
}
|
|
99
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
|
+
|
|
100
122
|
function hasAdImmediatelyAfter(el) {
|
|
101
123
|
const n = el && el.nextElementSibling;
|
|
102
124
|
return !!(n && n.classList && n.classList.contains(WRAP_CLASS));
|
|
103
125
|
}
|
|
104
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
|
+
|
|
105
147
|
function buildWrap(id, kind, afterPos) {
|
|
106
148
|
const wrap = document.createElement('div');
|
|
107
149
|
wrap.className = `${WRAP_CLASS} ${kind}`;
|
|
@@ -123,12 +165,6 @@
|
|
|
123
165
|
function safeRect(el) {
|
|
124
166
|
try { return el.getBoundingClientRect(); } catch (e) { return null; }
|
|
125
167
|
}
|
|
126
|
-
|
|
127
|
-
function destroyPlaceholder(id) {
|
|
128
|
-
try {
|
|
129
|
-
if (window.ezstandalone && typeof window.ezstandalone.destroyPlaceholders === 'function') {
|
|
130
|
-
window.ezstandalone.destroyPlaceholders([id]);
|
|
131
|
-
}
|
|
132
168
|
} catch (e) {}
|
|
133
169
|
}
|
|
134
170
|
|
|
@@ -216,41 +252,14 @@
|
|
|
216
252
|
if (attempts < 40) setTimeout(waitForPh, 50);
|
|
217
253
|
})();
|
|
218
254
|
}
|
|
219
|
-
|
|
220
|
-
function recycleOne(pool, usedSet, fifo, wrapperSelector) {
|
|
221
|
-
const margin = 1600;
|
|
222
|
-
const topLimit = -margin;
|
|
223
|
-
|
|
224
|
-
for (let i = 0; i < fifo.length; i++) {
|
|
225
|
-
const old = fifo[i];
|
|
226
|
-
const w = document.querySelector(wrapperSelector(old));
|
|
227
|
-
if (!w) { fifo.splice(i, 1); i--; continue; }
|
|
228
|
-
|
|
229
|
-
const r = safeRect(w);
|
|
230
|
-
if (r && r.bottom < topLimit) {
|
|
231
|
-
fifo.splice(i, 1);
|
|
232
|
-
w.remove();
|
|
233
|
-
usedSet.delete(old.id);
|
|
234
|
-
destroyPlaceholder(old.id);
|
|
235
|
-
pool.push(old.id);
|
|
236
|
-
return true;
|
|
237
|
-
}
|
|
238
255
|
}
|
|
239
256
|
return false;
|
|
240
257
|
}
|
|
241
258
|
|
|
242
259
|
function nextId(kind) {
|
|
243
|
-
const
|
|
244
|
-
const pool = isTopics ? state.poolTopics : state.poolPosts;
|
|
245
|
-
const used = isTopics ? state.usedTopics : state.usedPosts;
|
|
246
|
-
const fifo = isTopics ? state.fifoTopics : state.fifoPosts;
|
|
247
|
-
const sel = isTopics
|
|
248
|
-
? (old) => `.${WRAP_CLASS}.ezoic-ad-between[data-ezoic-after="${old.after}"]`
|
|
249
|
-
: (old) => `.${WRAP_CLASS}.ezoic-ad-message[data-ezoic-after="${old.after}"]`;
|
|
250
|
-
|
|
260
|
+
const pool = (kind === 'between') ? state.poolTopics : state.poolPosts;
|
|
251
261
|
if (pool.length) return pool.shift();
|
|
252
|
-
|
|
253
|
-
return null;
|
|
262
|
+
return null; // stop injecting when pool is empty
|
|
254
263
|
}
|
|
255
264
|
|
|
256
265
|
async function fetchConfig() {
|
|
@@ -285,7 +294,7 @@
|
|
|
285
294
|
const interval = Math.max(1, parseInt(cfg.intervalPosts, 10) || 6);
|
|
286
295
|
const first = normalizeBool(cfg.showFirstTopicAd);
|
|
287
296
|
|
|
288
|
-
const items =
|
|
297
|
+
const items = getTopicItems();
|
|
289
298
|
if (!items.length) return 0;
|
|
290
299
|
|
|
291
300
|
let inserted = 0;
|
|
@@ -310,7 +319,6 @@
|
|
|
310
319
|
const wrap = insertAfter(li, id, 'ezoic-ad-between', pos);
|
|
311
320
|
if (!wrap) continue;
|
|
312
321
|
|
|
313
|
-
state.fifoTopics.push({ id, after: pos });
|
|
314
322
|
inserted += 1;
|
|
315
323
|
|
|
316
324
|
callShowAdsWhenReady(id);
|
|
@@ -326,7 +334,7 @@
|
|
|
326
334
|
const interval = Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3);
|
|
327
335
|
const first = normalizeBool(cfg.showFirstMessageAd);
|
|
328
336
|
|
|
329
|
-
const posts =
|
|
337
|
+
const posts = getPostContainers();
|
|
330
338
|
if (!posts.length) return 0;
|
|
331
339
|
|
|
332
340
|
let inserted = 0;
|
|
@@ -350,7 +358,6 @@
|
|
|
350
358
|
const wrap = insertAfter(post, id, 'ezoic-ad-message', no);
|
|
351
359
|
if (!wrap) continue;
|
|
352
360
|
|
|
353
|
-
state.fifoPosts.push({ id, after: no });
|
|
354
361
|
inserted += 1;
|
|
355
362
|
|
|
356
363
|
callShowAdsWhenReady(id);
|
|
@@ -373,9 +380,6 @@
|
|
|
373
380
|
state.seenTopicAnchors = new WeakSet();
|
|
374
381
|
state.seenPostAnchors = new WeakSet();
|
|
375
382
|
|
|
376
|
-
state.fifoTopics = [];
|
|
377
|
-
state.fifoPosts = [];
|
|
378
|
-
|
|
379
383
|
state.lastShowById = new Map();
|
|
380
384
|
state.pendingById = new Set();
|
|
381
385
|
|
|
@@ -408,6 +412,8 @@
|
|
|
408
412
|
if (kind === 'topic') inserted = injectPosts(cfg);
|
|
409
413
|
else inserted = injectTopics(cfg);
|
|
410
414
|
|
|
415
|
+
enforceNoAdjacentAds();
|
|
416
|
+
|
|
411
417
|
// If we inserted max per run, schedule another pass to gradually fill (avoids “burst”)
|
|
412
418
|
if (inserted >= MAX_INSERTS_PER_RUN) {
|
|
413
419
|
setTimeout(() => scheduleRun('continue-fill'), 120);
|