nodebb-plugin-ezoic-infinite 0.6.7 → 0.6.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/public/client.js +108 -74
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -10,19 +10,27 @@ let debounceTimer;
|
|
|
10
10
|
let inFlight = false;
|
|
11
11
|
let rerunRequested = false;
|
|
12
12
|
|
|
13
|
-
// Per-page
|
|
13
|
+
// Per-page state (keyed by tid/cid)
|
|
14
14
|
let pageKey = null;
|
|
15
|
-
let seenSlots = new Set(); // slots already processed (never cleared on eviction)
|
|
16
|
-
let usedIds = new Set(); // IDs currently present in DOM
|
|
17
|
-
let adsFIFO = []; // [{slot, id}] oldest first
|
|
18
15
|
|
|
19
|
-
|
|
20
|
-
|
|
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
|
+
|
|
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
|
+
|
|
25
|
+
function resetState() {
|
|
26
|
+
seenAfterPostNo = new Set();
|
|
27
|
+
seenAfterTopicPos = new Set();
|
|
21
28
|
usedIds = new Set();
|
|
22
|
-
|
|
29
|
+
fifo = [];
|
|
30
|
+
fifoCat = [];
|
|
23
31
|
}
|
|
24
32
|
|
|
25
|
-
function
|
|
33
|
+
function getPageKey() {
|
|
26
34
|
try {
|
|
27
35
|
if (ajaxify && ajaxify.data) {
|
|
28
36
|
if (ajaxify.data.tid) return 'topic:' + ajaxify.data.tid;
|
|
@@ -58,11 +66,10 @@ function isCategoryTopicListPage() {
|
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
function getTopicPosts() {
|
|
61
|
-
// Only real posts
|
|
62
69
|
const $primary = $('[component="post"][data-pid]');
|
|
63
70
|
if ($primary.length) return $primary;
|
|
64
71
|
|
|
65
|
-
//
|
|
72
|
+
// fallback: top-level with post/content
|
|
66
73
|
return $('[data-pid]').filter(function () {
|
|
67
74
|
const $el = $(this);
|
|
68
75
|
const hasContent = $el.find('[component="post/content"]').length > 0;
|
|
@@ -94,7 +101,7 @@ function makeWrapperLike($target, classes, innerHtml, attrs) {
|
|
|
94
101
|
return '<div class="' + classes + '"' + attrStr + '>' + innerHtml + '</div>';
|
|
95
102
|
}
|
|
96
103
|
|
|
97
|
-
function
|
|
104
|
+
function cleanupOnNav() {
|
|
98
105
|
$('.ezoic-ad-post, .ezoic-ad-topic, .ezoic-ad-between').remove();
|
|
99
106
|
}
|
|
100
107
|
|
|
@@ -105,11 +112,26 @@ function pickNextId(pool) {
|
|
|
105
112
|
return null;
|
|
106
113
|
}
|
|
107
114
|
|
|
108
|
-
function
|
|
109
|
-
|
|
115
|
+
function removeOldestTopicAd() {
|
|
116
|
+
fifo.sort((a, b) => a.afterPostNo - b.afterPostNo);
|
|
117
|
+
const old = fifo.shift();
|
|
110
118
|
if (!old) return false;
|
|
111
119
|
|
|
112
|
-
const sel = '[data-ezoic-
|
|
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();
|
|
123
|
+
|
|
124
|
+
usedIds.delete(old.id);
|
|
125
|
+
// DO NOT delete seenAfterPostNo to prevent re-insertion in the top area
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function removeOldestCategoryAd() {
|
|
130
|
+
fifoCat.sort((a, b) => a.afterPos - b.afterPos);
|
|
131
|
+
const old = fifoCat.shift();
|
|
132
|
+
if (!old) return false;
|
|
133
|
+
|
|
134
|
+
const sel = '.ezoic-ad-topic[data-ezoic-after="' + old.afterPos + '"][data-ezoic-id="' + old.id + '"]';
|
|
113
135
|
const $el = $(sel);
|
|
114
136
|
if ($el.length) $el.remove();
|
|
115
137
|
|
|
@@ -135,7 +157,6 @@ function callEzoic(ids) {
|
|
|
135
157
|
|
|
136
158
|
window.ezstandalone.cmd.push(function () { run(); });
|
|
137
159
|
|
|
138
|
-
// Retry (Ezoic can load late)
|
|
139
160
|
let tries = 0;
|
|
140
161
|
const maxTries = 6;
|
|
141
162
|
const timer = setInterval(function () {
|
|
@@ -144,93 +165,109 @@ function callEzoic(ids) {
|
|
|
144
165
|
}, 800);
|
|
145
166
|
}
|
|
146
167
|
|
|
147
|
-
function
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
168
|
+
function getPostNumber($post) {
|
|
169
|
+
const di = parseInt($post.attr('data-index'), 10);
|
|
170
|
+
if (Number.isFinite(di) && di > 0) return di;
|
|
171
|
+
|
|
172
|
+
const txt = ($post.find('a.post-index').first().text() || '').trim();
|
|
173
|
+
const m = txt.match(/#\s*(\d+)/);
|
|
174
|
+
if (m) return parseInt(m[1], 10);
|
|
175
|
+
|
|
176
|
+
return NaN;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function getTopicPos($item) {
|
|
180
|
+
const pos = parseInt($item.attr('data-index'), 10);
|
|
181
|
+
if (Number.isFinite(pos) && pos >= 0) return pos + 1;
|
|
182
|
+
const schemaPos = parseInt($item.find('meta[itemprop="position"]').attr('content'), 10);
|
|
183
|
+
if (Number.isFinite(schemaPos) && schemaPos > 0) return schemaPos;
|
|
184
|
+
return NaN;
|
|
185
|
+
}
|
|
151
186
|
|
|
187
|
+
function injectTopicMessageAds($posts, pool, interval) {
|
|
152
188
|
const newIds = [];
|
|
153
189
|
|
|
154
|
-
|
|
155
|
-
|
|
190
|
+
$posts.each(function () {
|
|
191
|
+
const $p = $(this);
|
|
192
|
+
// Never insert after the last real post: it can break NodeBB infinite scroll
|
|
193
|
+
if ($p.is($posts.last())) return;
|
|
194
|
+
const postNo = getPostNumber($p);
|
|
195
|
+
if (!Number.isFinite(postNo) || postNo <= 0) return;
|
|
156
196
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (!$target.length) continue;
|
|
197
|
+
if (postNo % interval !== 0) return;
|
|
198
|
+
if (seenAfterPostNo.has(postNo)) return;
|
|
160
199
|
|
|
161
200
|
let id = pickNextId(pool);
|
|
162
201
|
if (!id) {
|
|
163
|
-
if (!
|
|
202
|
+
if (!removeOldestTopicAd()) return;
|
|
164
203
|
id = pickNextId(pool);
|
|
165
|
-
if (!id)
|
|
204
|
+
if (!id) return;
|
|
166
205
|
}
|
|
167
206
|
|
|
168
|
-
const
|
|
207
|
+
const inner = '<div class="ezoic-ad-message-inner"><div id="ezoic-pub-ad-placeholder-' + id + '"></div></div>';
|
|
169
208
|
const html = makeWrapperLike(
|
|
170
|
-
$
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
'data-ezoic-
|
|
209
|
+
$p,
|
|
210
|
+
'ezoic-ad-post ezoic-ad',
|
|
211
|
+
inner,
|
|
212
|
+
'data-ezoic-after="' + postNo + '" data-ezoic-id="' + id + '"'
|
|
174
213
|
);
|
|
175
214
|
|
|
176
|
-
$
|
|
215
|
+
$p.after(html);
|
|
177
216
|
|
|
178
|
-
|
|
217
|
+
seenAfterPostNo.add(postNo);
|
|
179
218
|
usedIds.add(id);
|
|
180
|
-
|
|
219
|
+
fifo.push({ afterPostNo: postNo, id: id });
|
|
181
220
|
newIds.push(id);
|
|
182
|
-
}
|
|
221
|
+
});
|
|
183
222
|
|
|
184
223
|
return newIds;
|
|
185
224
|
}
|
|
186
225
|
|
|
187
|
-
function
|
|
188
|
-
const total = $posts.length;
|
|
189
|
-
const maxSlot = Math.floor(total / interval);
|
|
190
|
-
if (maxSlot <= 0) return [];
|
|
191
|
-
|
|
226
|
+
function injectCategoryBetweenAds($items, pool, interval) {
|
|
192
227
|
const newIds = [];
|
|
193
228
|
|
|
194
|
-
|
|
195
|
-
|
|
229
|
+
$items.each(function () {
|
|
230
|
+
const $it = $(this);
|
|
231
|
+
// Never insert after the last real topic item (keeps NodeBB infinite scroll working)
|
|
232
|
+
if ($it.is($items.last())) return;
|
|
233
|
+
const pos = getTopicPos($it);
|
|
234
|
+
if (!Number.isFinite(pos) || pos <= 0) return;
|
|
196
235
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (!$target.length) continue;
|
|
236
|
+
if (pos % interval !== 0) return;
|
|
237
|
+
if (seenAfterTopicPos.has(pos)) return;
|
|
200
238
|
|
|
201
239
|
let id = pickNextId(pool);
|
|
202
240
|
if (!id) {
|
|
203
|
-
if (!
|
|
241
|
+
if (!removeOldestCategoryAd()) return;
|
|
204
242
|
id = pickNextId(pool);
|
|
205
|
-
if (!id)
|
|
243
|
+
if (!id) return;
|
|
206
244
|
}
|
|
207
245
|
|
|
208
|
-
|
|
209
|
-
const inner = '<div class="ezoic-ad-message-inner"><div id="ezoic-pub-ad-placeholder-' + id + '"></div></div>';
|
|
246
|
+
const placeholder = '<div id="ezoic-pub-ad-placeholder-' + id + '"></div>';
|
|
210
247
|
const html = makeWrapperLike(
|
|
211
|
-
$
|
|
212
|
-
'ezoic-ad-
|
|
213
|
-
|
|
214
|
-
'data-ezoic-
|
|
248
|
+
$it,
|
|
249
|
+
'ezoic-ad-topic ezoic-ad',
|
|
250
|
+
placeholder,
|
|
251
|
+
'data-ezoic-after="' + pos + '" data-ezoic-id="' + id + '"'
|
|
215
252
|
);
|
|
216
253
|
|
|
217
|
-
$
|
|
254
|
+
$it.after(html);
|
|
218
255
|
|
|
219
|
-
|
|
256
|
+
seenAfterTopicPos.add(pos);
|
|
220
257
|
usedIds.add(id);
|
|
221
|
-
|
|
258
|
+
fifoCat.push({ afterPos: pos, id: id });
|
|
222
259
|
newIds.push(id);
|
|
223
|
-
}
|
|
260
|
+
});
|
|
224
261
|
|
|
225
262
|
return newIds;
|
|
226
263
|
}
|
|
227
264
|
|
|
228
265
|
async function refreshAds() {
|
|
229
|
-
const key =
|
|
266
|
+
const key = getPageKey();
|
|
230
267
|
if (pageKey !== key) {
|
|
231
268
|
pageKey = key;
|
|
232
|
-
|
|
233
|
-
|
|
269
|
+
resetState();
|
|
270
|
+
cleanupOnNav();
|
|
234
271
|
}
|
|
235
272
|
|
|
236
273
|
if (inFlight) { rerunRequested = true; return; }
|
|
@@ -249,24 +286,21 @@ async function refreshAds() {
|
|
|
249
286
|
const onTopic = isTopicPage();
|
|
250
287
|
const onCategory = !onTopic && isCategoryTopicListPage();
|
|
251
288
|
|
|
252
|
-
const $posts = onTopic ? getTopicPosts() : $();
|
|
253
|
-
const $topicItems = onCategory ? getCategoryTopicItems() : $();
|
|
254
|
-
|
|
255
|
-
if (!$posts.length && !$topicItems.length) return;
|
|
256
|
-
|
|
257
289
|
const newIds = [];
|
|
258
290
|
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
|
|
291
|
+
if (onCategory) {
|
|
292
|
+
const $items = getCategoryTopicItems();
|
|
293
|
+
if (cfg.enableBetweenAds && betweenPool.length && $items.length) {
|
|
294
|
+
newIds.push(...injectCategoryBetweenAds($items, betweenPool, betweenInterval));
|
|
262
295
|
}
|
|
263
296
|
callEzoic(newIds);
|
|
264
297
|
return;
|
|
265
298
|
}
|
|
266
299
|
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
|
|
300
|
+
if (onTopic) {
|
|
301
|
+
const $posts = getTopicPosts();
|
|
302
|
+
if (cfg.enableMessageAds && messagePool.length && $posts.length) {
|
|
303
|
+
newIds.push(...injectTopicMessageAds($posts, messagePool, messageInterval));
|
|
270
304
|
}
|
|
271
305
|
callEzoic(newIds);
|
|
272
306
|
}
|
|
@@ -274,16 +308,16 @@ async function refreshAds() {
|
|
|
274
308
|
inFlight = false;
|
|
275
309
|
if (rerunRequested) {
|
|
276
310
|
rerunRequested = false;
|
|
277
|
-
setTimeout(refreshAds,
|
|
311
|
+
setTimeout(refreshAds, 160);
|
|
278
312
|
}
|
|
279
313
|
}
|
|
280
314
|
}
|
|
281
315
|
|
|
282
316
|
function debounceRefresh() {
|
|
283
317
|
clearTimeout(debounceTimer);
|
|
284
|
-
debounceTimer = setTimeout(refreshAds,
|
|
318
|
+
debounceTimer = setTimeout(refreshAds, 220);
|
|
285
319
|
}
|
|
286
320
|
|
|
287
321
|
$(document).ready(debounceRefresh);
|
|
288
322
|
$(window).on('action:ajaxify.end action:posts.loaded action:topic.loaded', debounceRefresh);
|
|
289
|
-
setTimeout(debounceRefresh,
|
|
323
|
+
setTimeout(debounceRefresh, 2200);
|