nodebb-plugin-ezoic-infinite 1.6.96 → 1.6.97
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 +9 -45
- package/package.json +1 -1
- package/public/client.js +69 -64
- package/public/style.css +18 -72
package/library.js
CHANGED
|
@@ -11,44 +11,25 @@ async function getSettings() {
|
|
|
11
11
|
return await meta.settings.get(SETTINGS_KEY);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* Vérifie l'exclusion par groupe avec gestion du format NodeBB
|
|
16
|
-
*/
|
|
17
14
|
async function isUserExcluded(uid, excludedGroups) {
|
|
18
15
|
if (!uid || uid <= 0) return false;
|
|
19
|
-
if (!excludedGroups ||
|
|
20
|
-
|
|
21
|
-
const excludedList = Array.isArray(excludedGroups) ? excludedGroups : [excludedGroups];
|
|
16
|
+
if (!excludedGroups || !excludedGroups.length) return false;
|
|
22
17
|
const userGroups = await groups.getUserGroupsNames([uid]);
|
|
23
|
-
|
|
24
|
-
// userGroups[0] contient le tableau des noms de groupes de l'utilisateur
|
|
25
|
-
return excludedList.some(g => userGroups[0].includes(g));
|
|
18
|
+
return excludedGroups.some(g => userGroups[0].includes(g));
|
|
26
19
|
}
|
|
27
20
|
|
|
28
21
|
async function getAllGroups() {
|
|
29
22
|
let names = await db.getSortedSetRange('groups:createtime', 0, -1);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
const filtered = names.filter(name => !groups.isPrivilegeGroup(name));
|
|
34
|
-
const data = await groups.getGroupsData(filtered);
|
|
35
|
-
const valid = data.filter(g => g && g.name);
|
|
36
|
-
valid.sort((a, b) => String(a.name).localeCompare(String(b.name), undefined, { sensitivity: 'base' }));
|
|
37
|
-
return valid;
|
|
23
|
+
const data = await groups.getGroupsData(names);
|
|
24
|
+
return data.filter(g => g && g.name).map(g => ({ name: g.name }));
|
|
38
25
|
}
|
|
39
26
|
|
|
40
27
|
plugin.init = async ({ router, middleware }) => {
|
|
41
28
|
async function render(req, res) {
|
|
42
29
|
const settings = await getSettings();
|
|
43
30
|
const allGroups = await getAllGroups();
|
|
44
|
-
|
|
45
31
|
res.render('admin/plugins/ezoic-infinite', {
|
|
46
|
-
title: 'Ezoic Infinite Ads',
|
|
47
32
|
...settings,
|
|
48
|
-
// On s'assure que les checkbox sont bien cochées dans l'admin
|
|
49
|
-
enableCategoryAds_checked: settings.enableCategoryAds === 'on' ? 'checked' : '',
|
|
50
|
-
enableBetweenAds_checked: settings.enableBetweenAds === 'on' ? 'checked' : '',
|
|
51
|
-
enableMessageAds_checked: settings.enableMessageAds === 'on' ? 'checked' : '',
|
|
52
33
|
allGroups,
|
|
53
34
|
});
|
|
54
35
|
}
|
|
@@ -56,47 +37,30 @@ plugin.init = async ({ router, middleware }) => {
|
|
|
56
37
|
router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, render);
|
|
57
38
|
router.get('/api/admin/plugins/ezoic-infinite', render);
|
|
58
39
|
|
|
59
|
-
// API consommée par le client.js
|
|
60
40
|
router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
|
|
61
41
|
const settings = await getSettings();
|
|
62
|
-
const excluded = await isUserExcluded(req.uid, settings.excludedGroups);
|
|
63
|
-
|
|
42
|
+
const excluded = await isUserExcluded(req.uid, settings.excludedGroups || []);
|
|
64
43
|
res.json({
|
|
65
44
|
excluded,
|
|
66
|
-
// Pool Accueil
|
|
67
45
|
enableCategoryAds: settings.enableCategoryAds === 'on',
|
|
68
46
|
showFirstCategoryAd: settings.showFirstCategoryAd === 'on',
|
|
69
|
-
categoryPlaceholderIds: settings.categoryPlaceholderIds
|
|
47
|
+
categoryPlaceholderIds: settings.categoryPlaceholderIds,
|
|
70
48
|
intervalCategories: parseInt(settings.intervalCategories, 10) || 5,
|
|
71
|
-
|
|
72
|
-
// Pool Topics (Catégories)
|
|
73
49
|
enableBetweenAds: settings.enableBetweenAds === 'on',
|
|
74
50
|
showFirstTopicAd: settings.showFirstTopicAd === 'on',
|
|
75
|
-
placeholderIds: settings.placeholderIds
|
|
51
|
+
placeholderIds: settings.placeholderIds,
|
|
76
52
|
intervalPosts: parseInt(settings.intervalPosts, 10) || 10,
|
|
77
|
-
|
|
78
|
-
// Pool Messages (Topics)
|
|
79
53
|
enableMessageAds: settings.enableMessageAds === 'on',
|
|
80
54
|
showFirstMessageAd: settings.showFirstMessageAd === 'on',
|
|
81
|
-
messagePlaceholderIds: settings.messagePlaceholderIds
|
|
55
|
+
messagePlaceholderIds: settings.messagePlaceholderIds,
|
|
82
56
|
messageIntervalPosts: parseInt(settings.messageIntervalPosts, 10) || 10,
|
|
83
57
|
});
|
|
84
58
|
});
|
|
85
59
|
};
|
|
86
60
|
|
|
87
61
|
plugin.addAdminNavigation = async (header) => {
|
|
88
|
-
header.plugins.push({
|
|
89
|
-
route: '/plugins/ezoic-infinite',
|
|
90
|
-
icon: 'fa-ad',
|
|
91
|
-
name: 'Ezoic Infinite'
|
|
92
|
-
});
|
|
62
|
+
header.plugins.push({ route: '/plugins/ezoic-infinite', icon: 'fa-ad', name: 'Ezoic Infinite' });
|
|
93
63
|
return header;
|
|
94
64
|
};
|
|
95
65
|
|
|
96
|
-
plugin.onSettingsSet = async (data) => {
|
|
97
|
-
if (data.plugin === 'ezoic-infinite') {
|
|
98
|
-
// Optionnel : purger un cache si nécessaire
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
|
|
102
66
|
module.exports = plugin;
|
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
const POOL_ID = 'nodebb-ezoic-placeholder-pool';
|
|
9
9
|
let config = null;
|
|
10
10
|
let isInternalChange = false;
|
|
11
|
+
let activeIds = new Set();
|
|
11
12
|
|
|
12
13
|
function getPool() {
|
|
13
14
|
let p = document.getElementById(POOL_ID);
|
|
@@ -20,68 +21,67 @@
|
|
|
20
21
|
return p;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
function forceFillAd(id) {
|
|
24
|
+
function callEzoic(id) {
|
|
25
25
|
if (typeof window.ezstandalone === 'undefined') return;
|
|
26
|
+
const pid = parseInt(id, 10);
|
|
27
|
+
if (isNaN(pid)) return;
|
|
26
28
|
|
|
27
29
|
window.ezstandalone.cmd.push(function() {
|
|
28
|
-
|
|
29
|
-
window.ezstandalone.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
// On force un rafraîchissement spécifique si showAds ne suffit pas
|
|
37
|
-
if (window.ezstandalone.refresh) {
|
|
38
|
-
window.ezstandalone.refresh(parseInt(id, 10));
|
|
39
|
-
}
|
|
40
|
-
} catch (e) {
|
|
41
|
-
console.warn('[Ezoic] Fill failed for', id, e);
|
|
42
|
-
}
|
|
43
|
-
}, 200);
|
|
30
|
+
window.ezstandalone.define(pid);
|
|
31
|
+
if (!window.ezstandalone.enabled) {
|
|
32
|
+
window.ezstandalone.enable();
|
|
33
|
+
window.ezstandalone.showAds();
|
|
34
|
+
} else {
|
|
35
|
+
window.ezstandalone.showAds(pid);
|
|
36
|
+
}
|
|
37
|
+
activeIds.add(pid);
|
|
44
38
|
});
|
|
45
39
|
}
|
|
46
40
|
|
|
47
41
|
function redistribute() {
|
|
48
42
|
if (!config || config.excluded) return;
|
|
49
43
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
const int = parseInt(interval, 10) || 10;
|
|
58
|
-
const pool = getPool();
|
|
59
|
-
|
|
60
|
-
items.forEach((item, index) => {
|
|
61
|
-
const pos = index + 1;
|
|
62
|
-
const shouldHaveAd = (pos === 1 && showFirst) || (pos % int === 0);
|
|
63
|
-
const next = item.nextElementSibling;
|
|
64
|
-
|
|
65
|
-
if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
|
|
66
|
-
const available = pool.querySelector(`.${WRAP_CLASS}[data-kind="${kind}"]`);
|
|
67
|
-
if (available) {
|
|
68
|
-
isInternalChange = true;
|
|
69
|
-
// On insère l'élément
|
|
70
|
-
item.parentNode.insertBefore(available, item.nextSibling);
|
|
71
|
-
|
|
72
|
-
// On déclenche le remplissage publicitaire
|
|
73
|
-
const pid = available.getAttribute('data-placeholder-id');
|
|
74
|
-
forceFillAd(pid);
|
|
75
|
-
|
|
76
|
-
setTimeout(() => { isInternalChange = false; }, 50);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
};
|
|
44
|
+
// 1. Accueil / Catégories
|
|
45
|
+
const categoryItems = document.querySelectorAll('.category-item, [component="categories/category"]');
|
|
46
|
+
if (config.enableCategoryAds) process(Array.from(categoryItems), 'home', config.intervalCategories, config.showFirstCategoryAd, 'div');
|
|
47
|
+
|
|
48
|
+
// 2. Liste des Topics (Harmony utilise des <li>)
|
|
49
|
+
const topicItems = document.querySelectorAll('li[component="category/topic"]');
|
|
50
|
+
if (config.enableBetweenAds) process(Array.from(topicItems), 'topic-list', config.intervalPosts, config.showFirstTopicAd, 'li');
|
|
81
51
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (config.enableMessageAds) process(Array.from(
|
|
52
|
+
// 3. Messages (Posts)
|
|
53
|
+
const postItems = document.querySelectorAll('[component="post"]');
|
|
54
|
+
if (config.enableMessageAds) process(Array.from(postItems), 'message', config.messageIntervalPosts, config.showFirstMessageAd, 'div');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function process(items, kind, interval, showFirst, wrapperTag) {
|
|
58
|
+
const int = parseInt(interval, 10) || 10;
|
|
59
|
+
const pool = getPool();
|
|
60
|
+
|
|
61
|
+
items.forEach((item, index) => {
|
|
62
|
+
const pos = index + 1;
|
|
63
|
+
const shouldHaveAd = (pos === 1 && showFirst) || (pos % int === 0);
|
|
64
|
+
const next = item.nextElementSibling;
|
|
65
|
+
|
|
66
|
+
if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
|
|
67
|
+
const available = pool.querySelector(`[data-kind="${kind}"]`);
|
|
68
|
+
if (available) {
|
|
69
|
+
isInternalChange = true;
|
|
70
|
+
|
|
71
|
+
// Création d'un conteneur qui respecte le type de parent (li ou div)
|
|
72
|
+
const wrapper = document.createElement(wrapperTag);
|
|
73
|
+
wrapper.className = WRAP_CLASS + ' ezoic-added-content';
|
|
74
|
+
wrapper.setAttribute('data-kind', kind);
|
|
75
|
+
wrapper.setAttribute('data-placeholder-id', available.getAttribute('data-placeholder-id'));
|
|
76
|
+
wrapper.innerHTML = available.innerHTML;
|
|
77
|
+
|
|
78
|
+
item.parentNode.insertBefore(wrapper, item.nextSibling);
|
|
79
|
+
callEzoic(available.getAttribute('data-placeholder-id'));
|
|
80
|
+
|
|
81
|
+
setTimeout(() => { isInternalChange = false; }, 50);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function init() {
|
|
@@ -90,21 +90,16 @@
|
|
|
90
90
|
.then(data => {
|
|
91
91
|
config = data;
|
|
92
92
|
const pool = getPool();
|
|
93
|
-
|
|
94
93
|
const setup = (raw, kind) => {
|
|
95
94
|
if (!raw) return;
|
|
96
95
|
raw.split(/[\s,]+/).filter(Boolean).forEach(id => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
d.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}"></div>`;
|
|
103
|
-
pool.appendChild(d);
|
|
104
|
-
}
|
|
96
|
+
const d = document.createElement('div');
|
|
97
|
+
d.setAttribute('data-kind', kind);
|
|
98
|
+
d.setAttribute('data-placeholder-id', id);
|
|
99
|
+
d.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}"></div>`;
|
|
100
|
+
pool.appendChild(d);
|
|
105
101
|
});
|
|
106
102
|
};
|
|
107
|
-
|
|
108
103
|
setup(config.categoryPlaceholderIds, 'home');
|
|
109
104
|
setup(config.placeholderIds, 'topic-list');
|
|
110
105
|
setup(config.messagePlaceholderIds, 'message');
|
|
@@ -118,8 +113,18 @@
|
|
|
118
113
|
});
|
|
119
114
|
}
|
|
120
115
|
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
// GESTION NAVIGATION SANS RECHARGE (SPA)
|
|
117
|
+
window.addEventListener('action:ajaxify.start', function() {
|
|
118
|
+
if (typeof window.ezstandalone !== 'undefined' && activeIds.size > 0) {
|
|
119
|
+
window.ezstandalone.cmd.push(function() {
|
|
120
|
+
window.ezstandalone.destroyAll();
|
|
121
|
+
activeIds.clear();
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
window.addEventListener('action:ajaxify.end', function() {
|
|
127
|
+
setTimeout(redistribute, 600);
|
|
123
128
|
});
|
|
124
129
|
|
|
125
130
|
if (document.readyState === 'loading') {
|
package/public/style.css
CHANGED
|
@@ -1,91 +1,37 @@
|
|
|
1
|
-
/*
|
|
2
|
-
CONTAINER GLOBAL DES PUBS
|
|
3
|
-
============================================================ */
|
|
1
|
+
/* Isolation et Taille */
|
|
4
2
|
.nodebb-ezoic-wrap {
|
|
5
3
|
display: block !important;
|
|
6
4
|
width: 100% !important;
|
|
7
|
-
min-height: 250px;
|
|
5
|
+
min-height: 250px !important;
|
|
8
6
|
margin: 30px 0 !important;
|
|
9
|
-
padding: 0;
|
|
10
7
|
clear: both !important;
|
|
11
8
|
text-align: center;
|
|
12
|
-
|
|
13
|
-
overflow: hidden;
|
|
9
|
+
list-style: none !important; /* Pour les injections en <li> */
|
|
14
10
|
}
|
|
15
11
|
|
|
16
|
-
/*
|
|
17
|
-
.nodebb-ezoic-wrap
|
|
18
|
-
margin: 0 auto !important;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/* ============================================================
|
|
22
|
-
1. PAGE D'ACCUEIL (Liste des catégories)
|
|
23
|
-
============================================================ */
|
|
24
|
-
[component="categories/category"] + .nodebb-ezoic-wrap,
|
|
25
|
-
.category-item + .nodebb-ezoic-wrap {
|
|
26
|
-
margin: 40px 0 !important;
|
|
27
|
-
border-top: 1px solid rgba(0,0,0,0.05);
|
|
28
|
-
padding-top: 25px;
|
|
29
|
-
background: transparent;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/* ============================================================
|
|
33
|
-
2. PAGE CATÉGORIE (Liste des Topics / Sujets)
|
|
34
|
-
============================================================ */
|
|
35
|
-
/* Important : NodeBB Harmony utilise des <li> pour les topics.
|
|
36
|
-
On s'assure que notre wrap ne casse pas la structure de liste
|
|
37
|
-
mais s'affiche comme un bloc complet. */
|
|
38
|
-
li[component="category/topic"] + .nodebb-ezoic-wrap {
|
|
39
|
-
list-style: none !important;
|
|
40
|
-
margin: 0 !important;
|
|
41
|
-
padding: 20px 0 !important;
|
|
42
|
-
border-bottom: 1px solid rgba(0,0,0,0.05);
|
|
43
|
-
display: block !important;
|
|
12
|
+
/* Fix pour l'accueil Harmony */
|
|
13
|
+
li.nodebb-ezoic-wrap {
|
|
44
14
|
float: none !important;
|
|
45
|
-
|
|
15
|
+
padding: 20px 0;
|
|
16
|
+
border-bottom: 1px solid rgba(0,0,0,0.05);
|
|
46
17
|
}
|
|
47
18
|
|
|
48
|
-
/*
|
|
49
|
-
3. PAGE DES POSTS (Messages à l'intérieur d'un topic)
|
|
50
|
-
============================================================ */
|
|
19
|
+
/* Éviter que les pubs ne se collent aux messages */
|
|
51
20
|
[component="post"] + .nodebb-ezoic-wrap {
|
|
52
|
-
|
|
53
|
-
padding:
|
|
54
|
-
|
|
55
|
-
border-radius: 12px;
|
|
56
|
-
border: 1px inset rgba(0,0,0,0.03);
|
|
21
|
+
background: rgba(0,0,0,0.02);
|
|
22
|
+
padding: 20px;
|
|
23
|
+
border-radius: 10px;
|
|
57
24
|
}
|
|
58
25
|
|
|
59
|
-
/*
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
.nodebb-ezoic-wrap:empty {
|
|
65
|
-
min-height: 1px;
|
|
66
|
-
height: 1px;
|
|
26
|
+
/* Anti-remontée (Pile-up) */
|
|
27
|
+
.nodebb-ezoic-wrap::before, .nodebb-ezoic-wrap::after {
|
|
28
|
+
content: "";
|
|
29
|
+
display: table;
|
|
30
|
+
clear: both;
|
|
67
31
|
}
|
|
68
32
|
|
|
69
|
-
/*
|
|
33
|
+
/* Centrage forcé des iframes Ezoic */
|
|
70
34
|
.ezoic-ad {
|
|
71
|
-
margin:
|
|
35
|
+
margin: 0 auto !important;
|
|
72
36
|
display: block !important;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/* Mobile : Réduction des marges pour ne pas perdre trop de place */
|
|
76
|
-
@media (max-width: 767px) {
|
|
77
|
-
.nodebb-ezoic-wrap {
|
|
78
|
-
margin: 20px 0 !important;
|
|
79
|
-
min-height: 100px;
|
|
80
|
-
}
|
|
81
|
-
[component="post"] + .nodebb-ezoic-wrap {
|
|
82
|
-
padding: 10px;
|
|
83
|
-
margin: 25px 0 !important;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/* Empêche les sauts de ligne bizarres dans les listes Harmony */
|
|
88
|
-
.categories > li.nodebb-ezoic-wrap,
|
|
89
|
-
.category > li.nodebb-ezoic-wrap {
|
|
90
|
-
float: none !important;
|
|
91
37
|
}
|