nodebb-plugin-ezoic-infinite 0.6.6 → 0.6.8
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 +109 -82
- package/public/style.css +2 -0
package/package.json
CHANGED
package/plugin.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 injectedSlots = new Set(); // slot numbers already injected on this page
|
|
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;
|
|
@@ -59,22 +67,22 @@ function isCategoryTopicListPage() {
|
|
|
59
67
|
|
|
60
68
|
function getTopicPosts() {
|
|
61
69
|
const $primary = $('[component="post"][data-pid]');
|
|
62
|
-
if ($primary.length) return $primary
|
|
70
|
+
if ($primary.length) return $primary;
|
|
63
71
|
|
|
72
|
+
// fallback: top-level with post/content
|
|
64
73
|
return $('[data-pid]').filter(function () {
|
|
65
74
|
const $el = $(this);
|
|
66
75
|
const hasContent = $el.find('[component="post/content"]').length > 0;
|
|
67
76
|
const nested = $el.parents('[data-pid]').length > 0;
|
|
68
77
|
return hasContent && !nested;
|
|
69
|
-
})
|
|
78
|
+
});
|
|
70
79
|
}
|
|
71
80
|
|
|
72
81
|
function getCategoryTopicItems() {
|
|
73
|
-
return $('li[component="category/topic"]')
|
|
82
|
+
return $('li[component="category/topic"]');
|
|
74
83
|
}
|
|
75
84
|
|
|
76
|
-
//
|
|
77
|
-
// If posts/topics are in a <ul>/<ol>, the wrapper MUST be a <li> or the browser may move it outside the list (often to the top).
|
|
85
|
+
// If target's parent is UL/OL, wrapper MUST be LI (otherwise browser may move it to top)
|
|
78
86
|
function wrapperTagFor($target) {
|
|
79
87
|
if (!$target || !$target.length) return 'div';
|
|
80
88
|
const parentTag = ($target.parent().prop('tagName') || '').toUpperCase();
|
|
@@ -93,8 +101,8 @@ function makeWrapperLike($target, classes, innerHtml, attrs) {
|
|
|
93
101
|
return '<div class="' + classes + '"' + attrStr + '>' + innerHtml + '</div>';
|
|
94
102
|
}
|
|
95
103
|
|
|
96
|
-
function
|
|
97
|
-
$('.ezoic-ad-post, .ezoic-ad-
|
|
104
|
+
function cleanupOnNav() {
|
|
105
|
+
$('.ezoic-ad-post, .ezoic-ad-topic, .ezoic-ad-between').remove();
|
|
98
106
|
}
|
|
99
107
|
|
|
100
108
|
function pickNextId(pool) {
|
|
@@ -104,15 +112,29 @@ function pickNextId(pool) {
|
|
|
104
112
|
return null;
|
|
105
113
|
}
|
|
106
114
|
|
|
107
|
-
function
|
|
108
|
-
|
|
115
|
+
function removeOldestTopicAd() {
|
|
116
|
+
fifo.sort((a, b) => a.afterPostNo - b.afterPostNo);
|
|
117
|
+
const old = fifo.shift();
|
|
118
|
+
if (!old) return false;
|
|
119
|
+
|
|
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();
|
|
109
132
|
if (!old) return false;
|
|
110
133
|
|
|
111
|
-
const sel = '[data-ezoic-
|
|
134
|
+
const sel = '.ezoic-ad-topic[data-ezoic-after="' + old.afterPos + '"][data-ezoic-id="' + old.id + '"]';
|
|
112
135
|
const $el = $(sel);
|
|
113
136
|
if ($el.length) $el.remove();
|
|
114
137
|
|
|
115
|
-
injectedSlots.delete(old.slot);
|
|
116
138
|
usedIds.delete(old.id);
|
|
117
139
|
return true;
|
|
118
140
|
}
|
|
@@ -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,105 @@ 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
|
+
}
|
|
151
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
|
+
}
|
|
186
|
+
|
|
187
|
+
function injectTopicMessageAds($posts, pool, interval) {
|
|
152
188
|
const newIds = [];
|
|
153
189
|
|
|
154
|
-
|
|
155
|
-
|
|
190
|
+
$posts.each(function () {
|
|
191
|
+
const $p = $(this);
|
|
192
|
+
const postNo = getPostNumber($p);
|
|
193
|
+
if (!Number.isFinite(postNo) || postNo <= 0) return;
|
|
156
194
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (!$target.length) continue;
|
|
195
|
+
if (postNo % interval !== 0) return;
|
|
196
|
+
if (seenAfterPostNo.has(postNo)) return;
|
|
160
197
|
|
|
161
198
|
let id = pickNextId(pool);
|
|
162
199
|
if (!id) {
|
|
163
|
-
if (!
|
|
200
|
+
if (!removeOldestTopicAd()) return;
|
|
164
201
|
id = pickNextId(pool);
|
|
165
|
-
if (!id)
|
|
202
|
+
if (!id) return;
|
|
166
203
|
}
|
|
167
204
|
|
|
168
|
-
const
|
|
205
|
+
const inner = '<div class="ezoic-ad-message-inner"><div id="ezoic-pub-ad-placeholder-' + id + '"></div></div>';
|
|
169
206
|
const html = makeWrapperLike(
|
|
170
|
-
$
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
'data-ezoic-
|
|
207
|
+
$p,
|
|
208
|
+
'ezoic-ad-post ezoic-ad',
|
|
209
|
+
inner,
|
|
210
|
+
'data-ezoic-after="' + postNo + '" data-ezoic-id="' + id + '"'
|
|
174
211
|
);
|
|
175
212
|
|
|
176
|
-
$
|
|
213
|
+
$p.after(html);
|
|
177
214
|
|
|
178
|
-
|
|
215
|
+
seenAfterPostNo.add(postNo);
|
|
179
216
|
usedIds.add(id);
|
|
180
|
-
|
|
217
|
+
fifo.push({ afterPostNo: postNo, id: id });
|
|
181
218
|
newIds.push(id);
|
|
182
|
-
}
|
|
219
|
+
});
|
|
183
220
|
|
|
184
221
|
return newIds;
|
|
185
222
|
}
|
|
186
223
|
|
|
187
|
-
function
|
|
188
|
-
const total = $posts.length;
|
|
189
|
-
const maxSlot = Math.floor(total / interval);
|
|
190
|
-
if (maxSlot <= 0) return [];
|
|
191
|
-
|
|
224
|
+
function injectCategoryBetweenAds($items, pool, interval) {
|
|
192
225
|
const newIds = [];
|
|
193
226
|
|
|
194
|
-
|
|
195
|
-
|
|
227
|
+
$items.each(function () {
|
|
228
|
+
const $it = $(this);
|
|
229
|
+
const pos = getTopicPos($it);
|
|
230
|
+
if (!Number.isFinite(pos) || pos <= 0) return;
|
|
196
231
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (!$target.length) continue;
|
|
232
|
+
if (pos % interval !== 0) return;
|
|
233
|
+
if (seenAfterTopicPos.has(pos)) return;
|
|
200
234
|
|
|
201
235
|
let id = pickNextId(pool);
|
|
202
236
|
if (!id) {
|
|
203
|
-
if (!
|
|
237
|
+
if (!removeOldestCategoryAd()) return;
|
|
204
238
|
id = pickNextId(pool);
|
|
205
|
-
if (!id)
|
|
239
|
+
if (!id) return;
|
|
206
240
|
}
|
|
207
241
|
|
|
208
|
-
const
|
|
242
|
+
const placeholder = '<div id="ezoic-pub-ad-placeholder-' + id + '"></div>';
|
|
209
243
|
const html = makeWrapperLike(
|
|
210
|
-
$
|
|
211
|
-
'
|
|
212
|
-
|
|
213
|
-
'data-ezoic-
|
|
244
|
+
$it,
|
|
245
|
+
'ezoic-ad-topic ezoic-ad',
|
|
246
|
+
placeholder,
|
|
247
|
+
'data-ezoic-after="' + pos + '" data-ezoic-id="' + id + '"'
|
|
214
248
|
);
|
|
215
249
|
|
|
216
|
-
$
|
|
250
|
+
$it.after(html);
|
|
217
251
|
|
|
218
|
-
|
|
252
|
+
seenAfterTopicPos.add(pos);
|
|
219
253
|
usedIds.add(id);
|
|
220
|
-
|
|
254
|
+
fifoCat.push({ afterPos: pos, id: id });
|
|
221
255
|
newIds.push(id);
|
|
222
|
-
}
|
|
256
|
+
});
|
|
223
257
|
|
|
224
258
|
return newIds;
|
|
225
259
|
}
|
|
226
260
|
|
|
227
261
|
async function refreshAds() {
|
|
228
|
-
|
|
229
|
-
const key = currentPageKey();
|
|
262
|
+
const key = getPageKey();
|
|
230
263
|
if (pageKey !== key) {
|
|
231
264
|
pageKey = key;
|
|
232
|
-
|
|
233
|
-
|
|
265
|
+
resetState();
|
|
266
|
+
cleanupOnNav();
|
|
234
267
|
}
|
|
235
268
|
|
|
236
269
|
if (inFlight) { rerunRequested = true; return; }
|
|
@@ -249,27 +282,21 @@ async function refreshAds() {
|
|
|
249
282
|
const onTopic = isTopicPage();
|
|
250
283
|
const onCategory = !onTopic && isCategoryTopicListPage();
|
|
251
284
|
|
|
252
|
-
const $posts = onTopic ? getTopicPosts() : $();
|
|
253
|
-
const $topicItems = onCategory ? getCategoryTopicItems() : $();
|
|
254
|
-
|
|
255
|
-
if (!$posts.length && !$topicItems.length) return;
|
|
256
|
-
|
|
257
285
|
const newIds = [];
|
|
258
286
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
if (cfg.enableBetweenAds && betweenPool.length) {
|
|
264
|
-
newIds.push(...injectBetweenIncremental($topicItems, betweenPool, betweenInterval, 'ezoic-ad-topic'));
|
|
287
|
+
if (onCategory) {
|
|
288
|
+
const $items = getCategoryTopicItems();
|
|
289
|
+
if (cfg.enableBetweenAds && betweenPool.length && $items.length) {
|
|
290
|
+
newIds.push(...injectCategoryBetweenAds($items, betweenPool, betweenInterval));
|
|
265
291
|
}
|
|
266
292
|
callEzoic(newIds);
|
|
267
293
|
return;
|
|
268
294
|
}
|
|
269
295
|
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
296
|
+
if (onTopic) {
|
|
297
|
+
const $posts = getTopicPosts();
|
|
298
|
+
if (cfg.enableMessageAds && messagePool.length && $posts.length) {
|
|
299
|
+
newIds.push(...injectTopicMessageAds($posts, messagePool, messageInterval));
|
|
273
300
|
}
|
|
274
301
|
callEzoic(newIds);
|
|
275
302
|
}
|
|
@@ -277,16 +304,16 @@ async function refreshAds() {
|
|
|
277
304
|
inFlight = false;
|
|
278
305
|
if (rerunRequested) {
|
|
279
306
|
rerunRequested = false;
|
|
280
|
-
setTimeout(refreshAds,
|
|
307
|
+
setTimeout(refreshAds, 160);
|
|
281
308
|
}
|
|
282
309
|
}
|
|
283
310
|
}
|
|
284
311
|
|
|
285
312
|
function debounceRefresh() {
|
|
286
313
|
clearTimeout(debounceTimer);
|
|
287
|
-
debounceTimer = setTimeout(refreshAds,
|
|
314
|
+
debounceTimer = setTimeout(refreshAds, 220);
|
|
288
315
|
}
|
|
289
316
|
|
|
290
317
|
$(document).ready(debounceRefresh);
|
|
291
318
|
$(window).on('action:ajaxify.end action:posts.loaded action:topic.loaded', debounceRefresh);
|
|
292
|
-
setTimeout(debounceRefresh,
|
|
319
|
+
setTimeout(debounceRefresh, 2200);
|
package/public/style.css
ADDED