nodebb-plugin-ezoic-infinite 0.8.6 → 0.8.9
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/plugin.json +4 -1
- package/public/client.js +65 -233
- package/public/style.css +2 -3
package/package.json
CHANGED
package/plugin.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
/* globals ajaxify */
|
|
3
2
|
|
|
3
|
+
/* globals ajaxify */
|
|
4
4
|
window.ezoicInfiniteLoaded = true;
|
|
5
5
|
|
|
6
6
|
let cachedConfig;
|
|
@@ -10,23 +10,22 @@ let debounceTimer;
|
|
|
10
10
|
let inFlight = false;
|
|
11
11
|
let rerunRequested = false;
|
|
12
12
|
|
|
13
|
-
// Per-page state
|
|
13
|
+
// Per-page state (keyed by tid/cid)
|
|
14
14
|
let pageKey = null;
|
|
15
15
|
|
|
16
|
-
// Topic page: anchor ads to absolute post number
|
|
17
|
-
let seenAfterPostNo = new Set();
|
|
18
|
-
let usedIds = new Set();
|
|
19
|
-
let fifo = [];
|
|
16
|
+
// Topic page state: anchor ads to absolute post number (not DOM index)
|
|
17
|
+
let seenAfterPostNo = new Set(); // post numbers we've already inserted an ad after
|
|
18
|
+
let usedIds = new Set(); // ids currently in DOM
|
|
19
|
+
let fifo = []; // [{afterPostNo, id}]
|
|
20
20
|
|
|
21
|
-
// Category
|
|
22
|
-
let seenAfterTopicPos = new Set();
|
|
23
|
-
let fifoCat = [];
|
|
21
|
+
// Category page state: anchor to absolute topic position if available
|
|
22
|
+
let seenAfterTopicPos = new Set(); // topic positions we've inserted after
|
|
23
|
+
let fifoCat = []; // [{afterPos, id}]
|
|
24
24
|
|
|
25
25
|
function resetState() {
|
|
26
26
|
seenAfterPostNo = new Set();
|
|
27
27
|
seenAfterTopicPos = new Set();
|
|
28
|
-
|
|
29
|
-
usedIdsBetween = new Set();
|
|
28
|
+
usedIds = new Set();
|
|
30
29
|
fifo = [];
|
|
31
30
|
fifoCat = [];
|
|
32
31
|
}
|
|
@@ -51,7 +50,7 @@ function parsePool(raw) {
|
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
async function fetchConfig() {
|
|
54
|
-
if (cachedConfig && Date.now() - lastFetch <
|
|
53
|
+
if (cachedConfig && Date.now() - lastFetch < 10000) return cachedConfig;
|
|
55
54
|
const res = await fetch('/api/plugins/ezoic-infinite/config', { credentials: 'same-origin' });
|
|
56
55
|
cachedConfig = await res.json();
|
|
57
56
|
lastFetch = Date.now();
|
|
@@ -59,10 +58,7 @@ async function fetchConfig() {
|
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
function isTopicPage() {
|
|
62
|
-
|
|
63
|
-
if (ajaxify && ajaxify.data && ajaxify.data.tid) return true;
|
|
64
|
-
} catch (e) {}
|
|
65
|
-
return /^\/topic\//.test(window.location.pathname);
|
|
61
|
+
return $('[component="post/content"]').length > 0 || $('[component="post"][data-pid]').length > 0;
|
|
66
62
|
}
|
|
67
63
|
|
|
68
64
|
function isCategoryTopicListPage() {
|
|
@@ -73,7 +69,7 @@ function getTopicPosts() {
|
|
|
73
69
|
const $primary = $('[component="post"][data-pid]');
|
|
74
70
|
if ($primary.length) return $primary;
|
|
75
71
|
|
|
76
|
-
//
|
|
72
|
+
// fallback: top-level with post/content
|
|
77
73
|
return $('[data-pid]').filter(function () {
|
|
78
74
|
const $el = $(this);
|
|
79
75
|
const hasContent = $el.find('[component="post/content"]').length > 0;
|
|
@@ -86,7 +82,7 @@ function getCategoryTopicItems() {
|
|
|
86
82
|
return $('li[component="category/topic"]');
|
|
87
83
|
}
|
|
88
84
|
|
|
89
|
-
// If target's parent is UL/OL, wrapper
|
|
85
|
+
// If target's parent is UL/OL, wrapper MUST be LI (otherwise browser may move it to top)
|
|
90
86
|
function wrapperTagFor($target) {
|
|
91
87
|
if (!$target || !$target.length) return 'div';
|
|
92
88
|
const parentTag = ($target.parent().prop('tagName') || '').toUpperCase();
|
|
@@ -106,185 +102,69 @@ function makeWrapperLike($target, classes, innerHtml, attrs) {
|
|
|
106
102
|
}
|
|
107
103
|
|
|
108
104
|
function cleanupOnNav() {
|
|
109
|
-
$('.ezoic-ad-post, .ezoic-ad-topic').remove();
|
|
105
|
+
$('.ezoic-ad-post, .ezoic-ad-topic, .ezoic-ad-between').remove();
|
|
110
106
|
}
|
|
111
107
|
|
|
112
|
-
function pickNextId(pool
|
|
108
|
+
function pickNextId(pool) {
|
|
113
109
|
for (const id of pool) {
|
|
114
|
-
if (!
|
|
110
|
+
if (!usedIds.has(id)) return id;
|
|
115
111
|
}
|
|
116
112
|
return null;
|
|
117
|
-
} finally {
|
|
118
|
-
window.__ezoicRecycling = false;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function destroyEzoicId(id) {
|
|
123
|
-
window.__ezoicLastDestroy = window.__ezoicLastDestroy || {};
|
|
124
|
-
const now = Date.now();
|
|
125
|
-
if (window.__ezoicLastDestroy[id] && now - window.__ezoicLastDestroy[id] < 2000) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
window.__ezoicLastDestroy[id] = now;
|
|
129
|
-
try {
|
|
130
|
-
window.ezstandalone = window.ezstandalone || {};
|
|
131
|
-
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
132
|
-
const fn = function () {
|
|
133
|
-
try {
|
|
134
|
-
if (typeof window.ezstandalone.destroyPlaceholders === 'function') {
|
|
135
|
-
window.ezstandalone.destroyPlaceholders(id);
|
|
136
|
-
}
|
|
137
|
-
} catch (e) {}
|
|
138
|
-
};
|
|
139
|
-
if (typeof window.ezstandalone.destroyPlaceholders === 'function') fn();
|
|
140
|
-
else window.ezstandalone.cmd.push(fn);
|
|
141
|
-
} catch (e) {}
|
|
142
113
|
}
|
|
143
114
|
|
|
144
|
-
function
|
|
145
|
-
if (window.__ezoicRecycling) return null;
|
|
146
|
-
window.__ezoicRecycling = true;
|
|
147
|
-
try {
|
|
148
|
-
if (!fifo.length) return null;
|
|
115
|
+
function removeOldestTopicAd() {
|
|
149
116
|
fifo.sort((a, b) => a.afterPostNo - b.afterPostNo);
|
|
117
|
+
const old = fifo.shift();
|
|
118
|
+
if (!old) return false;
|
|
150
119
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const $el = $(sel);
|
|
155
|
-
if (!$el.length) continue;
|
|
120
|
+
const sel = '.ezoic-ad-post[data-ezoic-after="' + old.afterPostNo + '"][data-ezoic-id="' + old.id + '"]';
|
|
121
|
+
const $el = $(sel);
|
|
122
|
+
if ($el.length) $el.remove();
|
|
156
123
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if ($last.length && $el.prev().is($last)) {
|
|
161
|
-
fifo.push(old);
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
} catch (e) {}
|
|
165
|
-
|
|
166
|
-
$el.remove();
|
|
167
|
-
usedIdsMsg.delete(old.id);
|
|
168
|
-
destroyEzoicId(old.id);
|
|
169
|
-
return old.id;
|
|
170
|
-
}
|
|
171
|
-
return null;
|
|
172
|
-
} finally {
|
|
173
|
-
window.__ezoicRecycling = false;
|
|
174
|
-
}
|
|
124
|
+
usedIds.delete(old.id);
|
|
125
|
+
// DO NOT delete seenAfterPostNo to prevent re-insertion in the top area
|
|
126
|
+
return true;
|
|
175
127
|
}
|
|
176
128
|
|
|
177
|
-
function
|
|
178
|
-
if (window.__ezoicRecycling) return null;
|
|
179
|
-
window.__ezoicRecycling = true;
|
|
180
|
-
try {
|
|
181
|
-
if (!fifoCat.length) return null;
|
|
129
|
+
function removeOldestCategoryAd() {
|
|
182
130
|
fifoCat.sort((a, b) => a.afterPos - b.afterPos);
|
|
131
|
+
const old = fifoCat.shift();
|
|
132
|
+
if (!old) return false;
|
|
183
133
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const $el = $(sel);
|
|
188
|
-
if (!$el.length) continue;
|
|
134
|
+
const sel = '.ezoic-ad-topic[data-ezoic-after="' + old.afterPos + '"][data-ezoic-id="' + old.id + '"]';
|
|
135
|
+
const $el = $(sel);
|
|
136
|
+
if ($el.length) $el.remove();
|
|
189
137
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if ($last.length && $el.prev().is($last)) {
|
|
193
|
-
fifoCat.push(old);
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
} catch (e) {}
|
|
197
|
-
|
|
198
|
-
$el.remove();
|
|
199
|
-
usedIdsBetween.delete(old.id);
|
|
200
|
-
destroyEzoicId(old.id);
|
|
201
|
-
return old.id;
|
|
202
|
-
}
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function setupAdAutoHeight() {
|
|
207
|
-
if (window.__ezoicAutoHeightAttached) return;
|
|
208
|
-
window.__ezoicAutoHeightAttached = true;
|
|
209
|
-
|
|
210
|
-
try {
|
|
211
|
-
const ro = new ResizeObserver(function () {
|
|
212
|
-
document.querySelectorAll('.ezoic-ad').forEach(function (wrap) {
|
|
213
|
-
wrap.style.minHeight = '0px';
|
|
214
|
-
wrap.style.height = 'auto';
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
function attach() {
|
|
219
|
-
document.querySelectorAll('.ezoic-ad').forEach(function (wrap) {
|
|
220
|
-
if (wrap.__ezoicRO) return;
|
|
221
|
-
wrap.__ezoicRO = true;
|
|
222
|
-
ro.observe(wrap);
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
attach();
|
|
227
|
-
setTimeout(attach, 600);
|
|
228
|
-
setTimeout(attach, 1600);
|
|
229
|
-
} catch (e) {}
|
|
138
|
+
usedIds.delete(old.id);
|
|
139
|
+
return true;
|
|
230
140
|
}
|
|
231
141
|
|
|
232
142
|
function callEzoic(ids) {
|
|
143
|
+
// ids optional; if omitted, we will scan DOM for unrendered placeholders
|
|
233
144
|
window.ezstandalone = window.ezstandalone || {};
|
|
234
145
|
window.ezstandalone.cmd = window.ezstandalone.cmd || [];
|
|
235
146
|
|
|
236
|
-
const markAndFilterByIds = function (list) {
|
|
237
|
-
const out = [];
|
|
238
|
-
list.forEach(function (id) {
|
|
239
|
-
const sel = '.ezoic-ad[data-ezoic-id="' + id + '"]';
|
|
240
|
-
const wraps = document.querySelectorAll(sel);
|
|
241
|
-
let anyUnrendered = false;
|
|
242
|
-
wraps.forEach(function (w) {
|
|
243
|
-
if (w.getAttribute('data-ezoic-rendered') !== '1') {
|
|
244
|
-
anyUnrendered = true;
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
if (!wraps.length) {
|
|
248
|
-
// fallback: if wrapper is missing, allow showAds (Ezoic might still handle placeholder)
|
|
249
|
-
out.push(id);
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (anyUnrendered) {
|
|
253
|
-
wraps.forEach(function (w) { w.setAttribute('data-ezoic-rendered', '1'); });
|
|
254
|
-
out.push(id);
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
return out;
|
|
258
|
-
};
|
|
259
|
-
|
|
260
147
|
const collect = function () {
|
|
261
148
|
const list = [];
|
|
262
|
-
document.querySelectorAll('.ezoic-ad').forEach(function (
|
|
263
|
-
if (wrap.getAttribute('data-ezoic-rendered') === '1') return;
|
|
264
|
-
const ph = wrap.querySelector('[id^="ezoic-pub-ad-placeholder-"]');
|
|
265
|
-
if (!ph) return;
|
|
149
|
+
document.querySelectorAll('.ezoic-ad [id^="ezoic-pub-ad-placeholder-"]').forEach(function (ph) {
|
|
266
150
|
const idStr = ph.id.replace('ezoic-pub-ad-placeholder-', '');
|
|
267
151
|
const id = parseInt(idStr, 10);
|
|
268
152
|
if (!Number.isFinite(id) || id <= 0) return;
|
|
269
|
-
|
|
153
|
+
|
|
154
|
+
const wrap = ph.closest('.ezoic-ad');
|
|
155
|
+
if (!wrap) return;
|
|
156
|
+
if (wrap.getAttribute('data-ezoic-rendered') === '1') return;
|
|
157
|
+
|
|
270
158
|
list.push(id);
|
|
159
|
+
wrap.setAttribute('data-ezoic-rendered', '1');
|
|
271
160
|
});
|
|
161
|
+
// de-dupe
|
|
272
162
|
return Array.from(new Set(list));
|
|
273
163
|
};
|
|
274
164
|
|
|
275
|
-
const
|
|
276
|
-
const toShow = input ? markAndFilterByIds(input) : collect();
|
|
165
|
+
const toShow = (ids && ids.length) ? Array.from(new Set(ids)) : collect();
|
|
277
166
|
if (!toShow.length) return;
|
|
278
167
|
|
|
279
|
-
// De-dupe rapid duplicate calls (observer/poller can fire twice)
|
|
280
|
-
const key = toShow.join(',');
|
|
281
|
-
const now = Date.now();
|
|
282
|
-
if (window.__ezoicLastShowKey === key && (now - (window.__ezoicLastShowAt || 0)) < 1200) {
|
|
283
|
-
return;
|
|
284
|
-
}
|
|
285
|
-
window.__ezoicLastShowKey = key;
|
|
286
|
-
window.__ezoicLastShowAt = now;
|
|
287
|
-
|
|
288
168
|
const run = function () {
|
|
289
169
|
try {
|
|
290
170
|
if (typeof window.ezstandalone.showAds === 'function') {
|
|
@@ -321,15 +201,13 @@ function callEzoic(ids) {
|
|
|
321
201
|
}
|
|
322
202
|
|
|
323
203
|
function getPostNumber($post) {
|
|
324
|
-
|
|
204
|
+
const di = parseInt($post.attr('data-index'), 10);
|
|
205
|
+
if (Number.isFinite(di) && di > 0) return di;
|
|
206
|
+
|
|
325
207
|
const txt = ($post.find('a.post-index').first().text() || '').trim();
|
|
326
208
|
const m = txt.match(/#\s*(\d+)/);
|
|
327
209
|
if (m) return parseInt(m[1], 10);
|
|
328
210
|
|
|
329
|
-
// fallback data-index (often 0/1 based)
|
|
330
|
-
const di = parseInt($post.attr('data-index'), 10);
|
|
331
|
-
if (Number.isFinite(di) && di >= 0) return di + 1;
|
|
332
|
-
|
|
333
211
|
return NaN;
|
|
334
212
|
}
|
|
335
213
|
|
|
@@ -346,20 +224,16 @@ function injectTopicMessageAds($posts, pool, interval) {
|
|
|
346
224
|
|
|
347
225
|
$posts.each(function () {
|
|
348
226
|
const $p = $(this);
|
|
349
|
-
// Never insert after the last post
|
|
227
|
+
// Never insert after the last real post: it can break NodeBB infinite scroll
|
|
350
228
|
if ($p.is($posts.last())) return;
|
|
351
|
-
|
|
352
229
|
const postNo = getPostNumber($p);
|
|
353
230
|
if (!Number.isFinite(postNo) || postNo <= 0) return;
|
|
354
231
|
|
|
355
232
|
if (postNo % interval !== 0) return;
|
|
356
233
|
if (seenAfterPostNo.has(postNo)) return;
|
|
357
234
|
|
|
358
|
-
let id = pickNextId(pool
|
|
359
|
-
if (!id) {
|
|
360
|
-
id = recycleOldestTopicId($posts);
|
|
361
|
-
if (!id) return;
|
|
362
|
-
}
|
|
235
|
+
let id = pickNextId(pool);
|
|
236
|
+
if (!id) { return; }
|
|
363
237
|
|
|
364
238
|
const inner = '<div class="ezoic-ad-message-inner"><div id="ezoic-pub-ad-placeholder-' + id + '"></div></div>';
|
|
365
239
|
const html = makeWrapperLike(
|
|
@@ -372,7 +246,7 @@ function injectTopicMessageAds($posts, pool, interval) {
|
|
|
372
246
|
$p.after(html);
|
|
373
247
|
|
|
374
248
|
seenAfterPostNo.add(postNo);
|
|
375
|
-
|
|
249
|
+
usedIds.add(id);
|
|
376
250
|
fifo.push({ afterPostNo: postNo, id: id });
|
|
377
251
|
newIds.push(id);
|
|
378
252
|
});
|
|
@@ -385,20 +259,16 @@ function injectCategoryBetweenAds($items, pool, interval) {
|
|
|
385
259
|
|
|
386
260
|
$items.each(function () {
|
|
387
261
|
const $it = $(this);
|
|
388
|
-
// Never insert after the last topic item (
|
|
262
|
+
// Never insert after the last real topic item (keeps NodeBB infinite scroll working)
|
|
389
263
|
if ($it.is($items.last())) return;
|
|
390
|
-
|
|
391
264
|
const pos = getTopicPos($it);
|
|
392
265
|
if (!Number.isFinite(pos) || pos <= 0) return;
|
|
393
266
|
|
|
394
267
|
if (pos % interval !== 0) return;
|
|
395
268
|
if (seenAfterTopicPos.has(pos)) return;
|
|
396
269
|
|
|
397
|
-
let id = pickNextId(pool
|
|
398
|
-
if (!id) {
|
|
399
|
-
id = recycleOldestCategoryId($items);
|
|
400
|
-
if (!id) return;
|
|
401
|
-
}
|
|
270
|
+
let id = pickNextId(pool);
|
|
271
|
+
if (!id) { return; }
|
|
402
272
|
|
|
403
273
|
const placeholder = '<div id="ezoic-pub-ad-placeholder-' + id + '"></div>';
|
|
404
274
|
const html = makeWrapperLike(
|
|
@@ -411,7 +281,7 @@ function injectCategoryBetweenAds($items, pool, interval) {
|
|
|
411
281
|
$it.after(html);
|
|
412
282
|
|
|
413
283
|
seenAfterTopicPos.add(pos);
|
|
414
|
-
|
|
284
|
+
usedIds.add(id);
|
|
415
285
|
fifoCat.push({ afterPos: pos, id: id });
|
|
416
286
|
newIds.push(id);
|
|
417
287
|
});
|
|
@@ -420,9 +290,6 @@ function injectCategoryBetweenAds($items, pool, interval) {
|
|
|
420
290
|
}
|
|
421
291
|
|
|
422
292
|
async function refreshAds() {
|
|
423
|
-
const now = Date.now();
|
|
424
|
-
if (window.__ezoicRefreshThrottle && now - window.__ezoicRefreshThrottle < 250) return;
|
|
425
|
-
window.__ezoicRefreshThrottle = now;
|
|
426
293
|
const key = getPageKey();
|
|
427
294
|
if (pageKey !== key) {
|
|
428
295
|
pageKey = key;
|
|
@@ -443,20 +310,17 @@ async function refreshAds() {
|
|
|
443
310
|
const messagePool = parsePool(cfg.messagePlaceholderIds);
|
|
444
311
|
const messageInterval = Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3);
|
|
445
312
|
|
|
446
|
-
const hasTopicList = isCategoryTopicListPage();
|
|
447
313
|
const onTopic = isTopicPage();
|
|
448
|
-
const onCategory =
|
|
314
|
+
const onCategory = !onTopic && isCategoryTopicListPage();
|
|
449
315
|
|
|
450
316
|
const newIds = [];
|
|
451
317
|
|
|
452
|
-
// Rule: topic list => between only ; topic page => message only
|
|
453
318
|
if (onCategory) {
|
|
454
319
|
const $items = getCategoryTopicItems();
|
|
455
320
|
if (cfg.enableBetweenAds && betweenPool.length && $items.length) {
|
|
456
321
|
newIds.push(...injectCategoryBetweenAds($items, betweenPool, betweenInterval));
|
|
457
322
|
}
|
|
458
323
|
callEzoic(newIds);
|
|
459
|
-
// also ensure any unrendered placeholders are rendered
|
|
460
324
|
callEzoic();
|
|
461
325
|
return;
|
|
462
326
|
}
|
|
@@ -467,6 +331,7 @@ async function refreshAds() {
|
|
|
467
331
|
newIds.push(...injectTopicMessageAds($posts, messagePool, messageInterval));
|
|
468
332
|
}
|
|
469
333
|
callEzoic(newIds);
|
|
334
|
+
callEzoic();
|
|
470
335
|
}
|
|
471
336
|
} finally {
|
|
472
337
|
inFlight = false;
|
|
@@ -482,47 +347,28 @@ function debounceRefresh() {
|
|
|
482
347
|
debounceTimer = setTimeout(refreshAds, 220);
|
|
483
348
|
}
|
|
484
349
|
|
|
485
|
-
$(document).ready(
|
|
486
|
-
|
|
487
|
-
$(window).on('action:ajaxify.start', function (ev, data) {
|
|
488
|
-
// IMPORTANT: do not cleanup on infinite scroll loads; only when navigating to a different page.
|
|
489
|
-
try {
|
|
490
|
-
const targetUrl = (data && (data.url || data.href)) ? String(data.url || data.href) : '';
|
|
491
|
-
if (targetUrl) {
|
|
492
|
-
const a = document.createElement('a');
|
|
493
|
-
a.href = targetUrl;
|
|
494
|
-
const targetPath = a.pathname || targetUrl;
|
|
495
|
-
// If we're staying on the same logical page (same path), don't wipe ads/state
|
|
496
|
-
if (targetPath === window.location.pathname) {
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
} catch (e) {}
|
|
501
|
-
pageKey = null;
|
|
502
|
-
resetState();
|
|
503
|
-
cleanupOnNav();
|
|
504
|
-
});
|
|
350
|
+
$(document).ready(debounceRefresh);
|
|
505
351
|
$(window).on('action:ajaxify.end action:posts.loaded action:topic.loaded action:topics.loaded action:category.loaded', debounceRefresh);
|
|
352
|
+
setTimeout(debounceRefresh, 2200);
|
|
506
353
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
// Observer + poller: ensure we rerun when NodeBB injects new items
|
|
354
|
+
// Fallback: some themes/devices don't emit the expected events for infinite scroll.
|
|
355
|
+
// Observe DOM additions and trigger a refresh when new posts/topics are appended/prepended.
|
|
510
356
|
(function setupEzoicObserver() {
|
|
511
357
|
if (window.__ezoicInfiniteObserver) return;
|
|
512
358
|
try {
|
|
513
|
-
const trigger = function () { debounceRefresh(); };
|
|
514
|
-
|
|
515
359
|
const obs = new MutationObserver(function (mutations) {
|
|
516
360
|
for (const m of mutations) {
|
|
517
361
|
if (!m.addedNodes || !m.addedNodes.length) continue;
|
|
518
362
|
for (const n of m.addedNodes) {
|
|
519
363
|
if (!n || n.nodeType !== 1) continue;
|
|
364
|
+
// direct match
|
|
520
365
|
if (n.matches && (n.matches('[component="post"][data-pid]') || n.matches('li[component="category/topic"]'))) {
|
|
521
|
-
|
|
366
|
+
debounceRefresh();
|
|
522
367
|
return;
|
|
523
368
|
}
|
|
369
|
+
// descendant match
|
|
524
370
|
if (n.querySelector && (n.querySelector('[component="post"][data-pid]') || n.querySelector('li[component="category/topic"]'))) {
|
|
525
|
-
|
|
371
|
+
debounceRefresh();
|
|
526
372
|
return;
|
|
527
373
|
}
|
|
528
374
|
}
|
|
@@ -530,19 +376,5 @@ setTimeout(function () { setupAdAutoHeight(); debounceRefresh(); }, 2200);
|
|
|
530
376
|
});
|
|
531
377
|
obs.observe(document.body, { childList: true, subtree: true });
|
|
532
378
|
window.__ezoicInfiniteObserver = obs;
|
|
533
|
-
|
|
534
|
-
let lastPostCount = 0;
|
|
535
|
-
let lastTopicCount = 0;
|
|
536
|
-
setInterval(function () {
|
|
537
|
-
try {
|
|
538
|
-
const posts = document.querySelectorAll('[component="post"][data-pid]').length;
|
|
539
|
-
const topics = document.querySelectorAll('li[component="category/topic"]').length;
|
|
540
|
-
if (posts !== lastPostCount || topics !== lastTopicCount) {
|
|
541
|
-
lastPostCount = posts;
|
|
542
|
-
lastTopicCount = topics;
|
|
543
|
-
trigger();
|
|
544
|
-
}
|
|
545
|
-
} catch (e) {}
|
|
546
|
-
}, 1500);
|
|
547
379
|
} catch (e) {}
|
|
548
380
|
})();
|
package/public/style.css
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
.ezoic-ad{
|
|
2
|
-
.ezoic-ad-
|
|
3
|
-
.ezoic-ad-message-inner{padding:0;margin:0;}
|
|
1
|
+
.ezoic-ad-post{margin:0.75rem 0;}
|
|
2
|
+
.ezoic-ad-message-inner{padding:0.75rem 0;}
|