nodebb-plugin-ezoic-infinite 1.3.0 → 1.4.0
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/library.js +15 -1
- package/package.json +1 -1
- package/public/client.js +65 -72
- package/public/templates/admin/plugins/ezoic-infinite.tpl +26 -0
package/library.js
CHANGED
|
@@ -34,13 +34,21 @@ async function getAllGroups() {
|
|
|
34
34
|
async function getSettings() {
|
|
35
35
|
const s = await meta.settings.get(SETTINGS_KEY);
|
|
36
36
|
return {
|
|
37
|
-
// Between-post ads (simple blocks)
|
|
37
|
+
// Between-post ads (simple blocks) in category topic list
|
|
38
38
|
enableBetweenAds: parseBool(s.enableBetweenAds, true),
|
|
39
|
+
showFirstTopicAd: parseBool(s.showFirstTopicAd, false),
|
|
39
40
|
placeholderIds: (s.placeholderIds || '').trim(),
|
|
40
41
|
intervalPosts: Math.max(1, parseInt(s.intervalPosts, 10) || 6),
|
|
41
42
|
|
|
43
|
+
// Home/categories list ads (between categories on / or /categories)
|
|
44
|
+
enableCategoryAds: parseBool(s.enableCategoryAds, false),
|
|
45
|
+
showFirstCategoryAd: parseBool(s.showFirstCategoryAd, false),
|
|
46
|
+
categoryPlaceholderIds: (s.categoryPlaceholderIds || '').trim(),
|
|
47
|
+
intervalCategories: Math.max(1, parseInt(s.intervalCategories, 10) || 4),
|
|
48
|
+
|
|
42
49
|
// "Ad message" between replies (looks like a post)
|
|
43
50
|
enableMessageAds: parseBool(s.enableMessageAds, false),
|
|
51
|
+
showFirstMessageAd: parseBool(s.showFirstMessageAd, false),
|
|
44
52
|
messagePlaceholderIds: (s.messagePlaceholderIds || '').trim(),
|
|
45
53
|
messageIntervalPosts: Math.max(1, parseInt(s.messageIntervalPosts, 10) || 3),
|
|
46
54
|
|
|
@@ -88,9 +96,15 @@ plugin.init = async ({ router, middleware }) => {
|
|
|
88
96
|
res.json({
|
|
89
97
|
excluded,
|
|
90
98
|
enableBetweenAds: settings.enableBetweenAds,
|
|
99
|
+
showFirstTopicAd: settings.showFirstTopicAd,
|
|
91
100
|
placeholderIds: settings.placeholderIds,
|
|
92
101
|
intervalPosts: settings.intervalPosts,
|
|
102
|
+
enableCategoryAds: settings.enableCategoryAds,
|
|
103
|
+
showFirstCategoryAd: settings.showFirstCategoryAd,
|
|
104
|
+
categoryPlaceholderIds: settings.categoryPlaceholderIds,
|
|
105
|
+
intervalCategories: settings.intervalCategories,
|
|
93
106
|
enableMessageAds: settings.enableMessageAds,
|
|
107
|
+
showFirstMessageAd: settings.showFirstMessageAd,
|
|
94
108
|
messagePlaceholderIds: settings.messagePlaceholderIds,
|
|
95
109
|
messageIntervalPosts: settings.messageIntervalPosts,
|
|
96
110
|
});
|
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/* eslint-disable no-console */
|
|
2
1
|
(function () {
|
|
3
2
|
'use strict';
|
|
4
3
|
|
|
@@ -7,6 +6,7 @@
|
|
|
7
6
|
const SELECTORS = {
|
|
8
7
|
topicItem: 'li[component="category/topic"]',
|
|
9
8
|
postItem: '[component="post"][data-pid]',
|
|
9
|
+
categoryItem: 'li[component="categories/category"]',
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
const WRAP_CLASS = 'ezoic-ad';
|
|
@@ -21,9 +21,11 @@
|
|
|
21
21
|
|
|
22
22
|
poolTopics: [],
|
|
23
23
|
poolPosts: [],
|
|
24
|
+
poolCategories: [],
|
|
24
25
|
|
|
25
26
|
usedTopics: new Set(),
|
|
26
27
|
usedPosts: new Set(),
|
|
28
|
+
usedCategories: new Set(),
|
|
27
29
|
|
|
28
30
|
lastShowById: new Map(),
|
|
29
31
|
pendingById: new Set(),
|
|
@@ -72,15 +74,23 @@
|
|
|
72
74
|
function getKind() {
|
|
73
75
|
const p = window.location.pathname || '';
|
|
74
76
|
if (/^\/topic\//.test(p)) return 'topic';
|
|
75
|
-
if (/^\/category\//.test(p)) return '
|
|
76
|
-
if (
|
|
77
|
-
|
|
77
|
+
if (/^\/category\//.test(p)) return 'categoryTopics';
|
|
78
|
+
if (p === '/' || /^\/categories/.test(p)) return 'categories';
|
|
79
|
+
// fallback by DOM
|
|
80
|
+
if (document.querySelector(SELECTORS.categoryItem)) return 'categories';
|
|
81
|
+
if (document.querySelector(SELECTORS.postItem)) return 'topic';
|
|
82
|
+
if (document.querySelector(SELECTORS.topicItem)) return 'categoryTopics';
|
|
83
|
+
return 'other';
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
function getTopicItems() {
|
|
81
87
|
return Array.from(document.querySelectorAll(SELECTORS.topicItem));
|
|
82
88
|
}
|
|
83
89
|
|
|
90
|
+
function getCategoryItems() {
|
|
91
|
+
return Array.from(document.querySelectorAll(SELECTORS.categoryItem));
|
|
92
|
+
}
|
|
93
|
+
|
|
84
94
|
function getPostContainers() {
|
|
85
95
|
const nodes = Array.from(document.querySelectorAll(SELECTORS.postItem));
|
|
86
96
|
return nodes.filter((el) => {
|
|
@@ -93,9 +103,9 @@
|
|
|
93
103
|
});
|
|
94
104
|
}
|
|
95
105
|
|
|
96
|
-
function buildWrap(id,
|
|
106
|
+
function buildWrap(id, kindClass, afterPos) {
|
|
97
107
|
const wrap = document.createElement('div');
|
|
98
|
-
wrap.className = `${WRAP_CLASS} ${
|
|
108
|
+
wrap.className = `${WRAP_CLASS} ${kindClass}`;
|
|
99
109
|
wrap.setAttribute('data-ezoic-after', String(afterPos));
|
|
100
110
|
wrap.style.width = '100%';
|
|
101
111
|
|
|
@@ -122,6 +132,7 @@
|
|
|
122
132
|
try {
|
|
123
133
|
state.usedTopics.forEach((id) => ids.push(id));
|
|
124
134
|
state.usedPosts.forEach((id) => ids.push(id));
|
|
135
|
+
state.usedCategories.forEach((id) => ids.push(id));
|
|
125
136
|
} catch (e) {}
|
|
126
137
|
|
|
127
138
|
if (!ids.length) return;
|
|
@@ -229,7 +240,7 @@
|
|
|
229
240
|
}
|
|
230
241
|
|
|
231
242
|
function nextId(kind) {
|
|
232
|
-
const pool = (kind === 'between') ? state.poolTopics : state.poolPosts;
|
|
243
|
+
const pool = (kind === 'between') ? state.poolTopics : (kind === 'message') ? state.poolPosts : state.poolCategories;
|
|
233
244
|
if (pool.length) return pool.shift();
|
|
234
245
|
return null;
|
|
235
246
|
}
|
|
@@ -257,6 +268,7 @@
|
|
|
257
268
|
function initPools(cfg) {
|
|
258
269
|
if (state.poolTopics.length === 0) state.poolTopics = parsePool(cfg.placeholderIds);
|
|
259
270
|
if (state.poolPosts.length === 0) state.poolPosts = parsePool(cfg.messagePlaceholderIds);
|
|
271
|
+
if (state.poolCategories.length === 0) state.poolCategories = parsePool(cfg.categoryPlaceholderIds);
|
|
260
272
|
}
|
|
261
273
|
|
|
262
274
|
function computeTargets(count, interval, showFirst) {
|
|
@@ -266,75 +278,31 @@
|
|
|
266
278
|
for (let i = 1; i <= count; i++) {
|
|
267
279
|
if (i % interval === 0) out.push(i);
|
|
268
280
|
}
|
|
269
|
-
// uniq + sort
|
|
270
281
|
return Array.from(new Set(out)).sort((a, b) => a - b);
|
|
271
282
|
}
|
|
272
283
|
|
|
273
|
-
function
|
|
274
|
-
if (!normalizeBool(cfg.enableBetweenAds)) return 0;
|
|
275
|
-
|
|
276
|
-
const interval = Math.max(1, parseInt(cfg.intervalPosts, 10) || 6);
|
|
277
|
-
const showFirst = normalizeBool(cfg.showFirstTopicAd);
|
|
278
|
-
|
|
279
|
-
const items = getTopicItems();
|
|
284
|
+
function injectBetween(kindClass, items, interval, showFirst, kindPool, usedSet) {
|
|
280
285
|
if (!items.length) return 0;
|
|
281
|
-
|
|
282
286
|
const targets = computeTargets(items.length, interval, showFirst);
|
|
283
287
|
|
|
284
288
|
let inserted = 0;
|
|
285
289
|
for (const afterPos of targets) {
|
|
286
290
|
if (inserted >= MAX_INSERTS_PER_RUN) break;
|
|
287
291
|
|
|
288
|
-
const
|
|
289
|
-
if (!
|
|
292
|
+
const el = items[afterPos - 1];
|
|
293
|
+
if (!el || !el.isConnected) continue;
|
|
290
294
|
|
|
291
|
-
//
|
|
292
|
-
const prevWrap = findWrap(
|
|
295
|
+
// Prevent back-to-back at load
|
|
296
|
+
const prevWrap = findWrap(kindClass, afterPos - 1);
|
|
293
297
|
if (prevWrap) continue;
|
|
294
298
|
|
|
295
|
-
if (findWrap(
|
|
299
|
+
if (findWrap(kindClass, afterPos)) continue;
|
|
296
300
|
|
|
297
|
-
const id = nextId(
|
|
301
|
+
const id = nextId(kindPool);
|
|
298
302
|
if (!id) break;
|
|
299
303
|
|
|
300
|
-
|
|
301
|
-
const wrap = insertAfter(
|
|
302
|
-
if (!wrap) continue;
|
|
303
|
-
|
|
304
|
-
callShowAdsWhenReady(id);
|
|
305
|
-
inserted += 1;
|
|
306
|
-
}
|
|
307
|
-
return inserted;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function injectPosts(cfg) {
|
|
311
|
-
if (!normalizeBool(cfg.enableMessageAds)) return 0;
|
|
312
|
-
|
|
313
|
-
const interval = Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3);
|
|
314
|
-
const showFirst = normalizeBool(cfg.showFirstMessageAd);
|
|
315
|
-
|
|
316
|
-
const posts = getPostContainers();
|
|
317
|
-
if (!posts.length) return 0;
|
|
318
|
-
|
|
319
|
-
const targets = computeTargets(posts.length, interval, showFirst);
|
|
320
|
-
|
|
321
|
-
let inserted = 0;
|
|
322
|
-
for (const afterPos of targets) {
|
|
323
|
-
if (inserted >= MAX_INSERTS_PER_RUN) break;
|
|
324
|
-
|
|
325
|
-
const post = posts[afterPos - 1];
|
|
326
|
-
if (!post || !post.isConnected) continue;
|
|
327
|
-
|
|
328
|
-
const prevWrap = findWrap('ezoic-ad-message', afterPos - 1);
|
|
329
|
-
if (prevWrap) continue;
|
|
330
|
-
|
|
331
|
-
if (findWrap('ezoic-ad-message', afterPos)) continue;
|
|
332
|
-
|
|
333
|
-
const id = nextId('message');
|
|
334
|
-
if (!id) break;
|
|
335
|
-
|
|
336
|
-
state.usedPosts.add(id);
|
|
337
|
-
const wrap = insertAfter(post, id, 'ezoic-ad-message', afterPos);
|
|
304
|
+
usedSet.add(id);
|
|
305
|
+
const wrap = insertAfter(el, id, kindClass, afterPos);
|
|
338
306
|
if (!wrap) continue;
|
|
339
307
|
|
|
340
308
|
callShowAdsWhenReady(id);
|
|
@@ -344,7 +312,6 @@
|
|
|
344
312
|
}
|
|
345
313
|
|
|
346
314
|
function enforceNoAdjacentAds() {
|
|
347
|
-
// If DOM changes produce adjacent wrappers (rare but possible), hide the later one.
|
|
348
315
|
const ads = Array.from(document.querySelectorAll(`.${WRAP_CLASS}`));
|
|
349
316
|
for (let i = 0; i < ads.length; i++) {
|
|
350
317
|
const ad = ads[i];
|
|
@@ -363,8 +330,10 @@
|
|
|
363
330
|
|
|
364
331
|
state.poolTopics = [];
|
|
365
332
|
state.poolPosts = [];
|
|
333
|
+
state.poolCategories = [];
|
|
366
334
|
state.usedTopics.clear();
|
|
367
335
|
state.usedPosts.clear();
|
|
336
|
+
state.usedCategories.clear();
|
|
368
337
|
|
|
369
338
|
state.lastShowById = new Map();
|
|
370
339
|
state.pendingById = new Set();
|
|
@@ -381,7 +350,7 @@
|
|
|
381
350
|
|
|
382
351
|
function ensureObserver() {
|
|
383
352
|
if (state.obs) return;
|
|
384
|
-
state.obs = new MutationObserver(() => scheduleRun('
|
|
353
|
+
state.obs = new MutationObserver(() => scheduleRun('mutation'));
|
|
385
354
|
try { state.obs.observe(document.body, { childList: true, subtree: true }); } catch (e) {}
|
|
386
355
|
setTimeout(() => { if (state.obs) { try { state.obs.disconnect(); } catch (e) {} state.obs = null; } }, 15000);
|
|
387
356
|
}
|
|
@@ -397,22 +366,47 @@
|
|
|
397
366
|
const kind = getKind();
|
|
398
367
|
let inserted = 0;
|
|
399
368
|
|
|
400
|
-
if (kind === 'topic')
|
|
401
|
-
|
|
369
|
+
if (kind === 'topic') {
|
|
370
|
+
if (normalizeBool(cfg.enableMessageAds)) {
|
|
371
|
+
inserted = injectBetween('ezoic-ad-message', getPostContainers(),
|
|
372
|
+
Math.max(1, parseInt(cfg.messageIntervalPosts, 10) || 3),
|
|
373
|
+
normalizeBool(cfg.showFirstMessageAd),
|
|
374
|
+
'message',
|
|
375
|
+
state.usedPosts);
|
|
376
|
+
}
|
|
377
|
+
} else if (kind === 'categoryTopics') {
|
|
378
|
+
if (normalizeBool(cfg.enableBetweenAds)) {
|
|
379
|
+
inserted = injectBetween('ezoic-ad-between', getTopicItems(),
|
|
380
|
+
Math.max(1, parseInt(cfg.intervalPosts, 10) || 6),
|
|
381
|
+
normalizeBool(cfg.showFirstTopicAd),
|
|
382
|
+
'between',
|
|
383
|
+
state.usedTopics);
|
|
384
|
+
}
|
|
385
|
+
} else if (kind === 'categories') {
|
|
386
|
+
if (normalizeBool(cfg.enableCategoryAds)) {
|
|
387
|
+
inserted = injectBetween('ezoic-ad-categories', getCategoryItems(),
|
|
388
|
+
Math.max(1, parseInt(cfg.intervalCategories, 10) || 4),
|
|
389
|
+
normalizeBool(cfg.showFirstCategoryAd),
|
|
390
|
+
'categories',
|
|
391
|
+
state.usedCategories);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
402
394
|
|
|
403
395
|
enforceNoAdjacentAds();
|
|
404
396
|
|
|
405
|
-
// If nothing inserted and
|
|
406
|
-
|
|
407
|
-
if (
|
|
397
|
+
// If nothing inserted and list isn't in DOM yet (first click), retry a bit
|
|
398
|
+
let count = 0;
|
|
399
|
+
if (kind === 'topic') count = getPostContainers().length;
|
|
400
|
+
else if (kind === 'categoryTopics') count = getTopicItems().length;
|
|
401
|
+
else if (kind === 'categories') count = getCategoryItems().length;
|
|
402
|
+
|
|
403
|
+
if (count === 0 && state.attempts < 25) {
|
|
408
404
|
state.attempts += 1;
|
|
409
405
|
setTimeout(() => scheduleRun('await-items'), 120);
|
|
410
406
|
return;
|
|
411
407
|
}
|
|
412
408
|
|
|
413
|
-
if (inserted >= MAX_INSERTS_PER_RUN)
|
|
414
|
-
setTimeout(() => scheduleRun('continue-fill'), 140);
|
|
415
|
-
}
|
|
409
|
+
if (inserted >= MAX_INSERTS_PER_RUN) setTimeout(() => scheduleRun('continue'), 140);
|
|
416
410
|
}
|
|
417
411
|
|
|
418
412
|
function scheduleRun() {
|
|
@@ -468,11 +462,10 @@
|
|
|
468
462
|
});
|
|
469
463
|
}
|
|
470
464
|
|
|
471
|
-
// Boot
|
|
472
465
|
cleanup();
|
|
473
466
|
bind();
|
|
474
467
|
ensureObserver();
|
|
475
468
|
state.pageKey = getPageKey();
|
|
476
469
|
scheduleRun();
|
|
477
470
|
setTimeout(scheduleRun, 250);
|
|
478
|
-
})();
|
|
471
|
+
})();
|
|
@@ -26,6 +26,32 @@
|
|
|
26
26
|
|
|
27
27
|
<hr/>
|
|
28
28
|
|
|
29
|
+
|
|
30
|
+
<hr/>
|
|
31
|
+
|
|
32
|
+
<h4 class="mt-3">Pubs entre les catégories (page d’accueil)</h4>
|
|
33
|
+
<p class="form-text">Insère des pubs entre les catégories sur la page d’accueil (liste des catégories).</p>
|
|
34
|
+
|
|
35
|
+
<div class="form-check mb-3">
|
|
36
|
+
<input class="form-check-input" type="checkbox" id="enableCategoryAds" name="enableCategoryAds">
|
|
37
|
+
<label class="form-check-label" for="enableCategoryAds">Activer les pubs entre les catégories</label>
|
|
38
|
+
<div class="form-check mt-2">
|
|
39
|
+
<input class="form-check-input" type="checkbox" name="showFirstCategoryAd" />
|
|
40
|
+
<label class="form-check-label">Afficher une pub après la 1ère catégorie</label>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div class="mb-3">
|
|
45
|
+
<label class="form-label" for="categoryPlaceholderIds">Pool d’IDs Ezoic (catégories)</label>
|
|
46
|
+
<textarea id="categoryPlaceholderIds" name="categoryPlaceholderIds" class="form-control" rows="4">{categoryPlaceholderIds}</textarea>
|
|
47
|
+
<p class="form-text">IDs numériques, un par ligne. Utilise un pool dédié (différent des pools topics/messages).</p>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="mb-3">
|
|
51
|
+
<label class="form-label" for="intervalCategories">Afficher une pub toutes les N catégories</label>
|
|
52
|
+
<input type="number" id="intervalCategories" name="intervalCategories" class="form-control" value="{intervalCategories}" min="1">
|
|
53
|
+
</div>
|
|
54
|
+
|
|
29
55
|
<h4 class="mt-3">Pubs “message” entre les réponses</h4>
|
|
30
56
|
<p class="form-text">Insère un bloc qui ressemble à un post, toutes les N réponses (dans une page topic).</p>
|
|
31
57
|
|