nodebb-plugin-ezoic-infinite 1.6.55 → 1.6.57
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 +38 -115
- package/package.json +1 -1
- package/public/client.js +79 -92
- package/public/style.css +61 -81
package/library.js
CHANGED
|
@@ -4,127 +4,50 @@ const meta = require.main.require('./src/meta');
|
|
|
4
4
|
const groups = require.main.require('./src/groups');
|
|
5
5
|
const db = require.main.require('./src/database');
|
|
6
6
|
|
|
7
|
-
const SETTINGS_KEY = 'ezoic-infinite';
|
|
8
7
|
const plugin = {};
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
if (!value) return [];
|
|
12
|
-
if (Array.isArray(value)) return value;
|
|
13
|
-
return String(value).split(',').map(s => s.trim()).filter(Boolean);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function parseBool(v, def = false) {
|
|
17
|
-
if (v === undefined || v === null || v === '') return def;
|
|
18
|
-
if (typeof v === 'boolean') return v;
|
|
19
|
-
const s = String(v).toLowerCase();
|
|
20
|
-
return s === '1' || s === 'true' || s === 'on' || s === 'yes';
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function getAllGroups() {
|
|
24
|
-
let names = await db.getSortedSetRange('groups:createtime', 0, -1);
|
|
25
|
-
if (!names || !names.length) {
|
|
26
|
-
names = await db.getSortedSetRange('groups:visible:createtime', 0, -1);
|
|
27
|
-
}
|
|
28
|
-
const filtered = names.filter(name => !groups.isPrivilegeGroup(name));
|
|
29
|
-
const data = await groups.getGroupsData(filtered);
|
|
30
|
-
// Filter out nulls (groups deleted between the sorted-set read and getGroupsData)
|
|
31
|
-
const valid = data.filter(g => g && g.name);
|
|
32
|
-
valid.sort((a, b) => String(a.name).localeCompare(String(b.name), undefined, { sensitivity: 'base' }));
|
|
33
|
-
return valid;
|
|
34
|
-
}
|
|
35
|
-
let _settingsCache = null;
|
|
36
|
-
let _settingsCacheAt = 0;
|
|
37
|
-
const SETTINGS_TTL = 30000; // 30s
|
|
38
|
-
|
|
9
|
+
// Helper pour garantir des valeurs par défaut
|
|
39
10
|
async function getSettings() {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
enableCategoryAds: parseBool(s.enableCategoryAds, false),
|
|
53
|
-
showFirstCategoryAd: parseBool(s.showFirstCategoryAd, false),
|
|
54
|
-
categoryPlaceholderIds: (s.categoryPlaceholderIds || '').trim(),
|
|
55
|
-
intervalCategories: Math.max(1, parseInt(s.intervalCategories, 10) || 4),
|
|
56
|
-
|
|
57
|
-
// "Ad message" between replies (looks like a post)
|
|
58
|
-
enableMessageAds: parseBool(s.enableMessageAds, false),
|
|
59
|
-
showFirstMessageAd: parseBool(s.showFirstMessageAd, false),
|
|
60
|
-
messagePlaceholderIds: (s.messagePlaceholderIds || '').trim(),
|
|
61
|
-
messageIntervalPosts: Math.max(1, parseInt(s.messageIntervalPosts, 10) || 3),
|
|
62
|
-
|
|
63
|
-
excludedGroups: normalizeExcludedGroups(s.excludedGroups),
|
|
64
|
-
};
|
|
65
|
-
return _settingsCache;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async function isUserExcluded(uid, excludedGroups) {
|
|
69
|
-
if (!uid || !excludedGroups.length) return false;
|
|
70
|
-
const userGroups = await groups.getUserGroups([uid]);
|
|
71
|
-
return (userGroups[0] || []).some(g => excludedGroups.includes(g.name));
|
|
11
|
+
const settings = await meta.settings.get('ezoic-infinite');
|
|
12
|
+
return {
|
|
13
|
+
enableBetweenAds: settings.enableBetweenAds === 'on',
|
|
14
|
+
showFirstTopicAd: settings.showFirstTopicAd === 'on',
|
|
15
|
+
placeholderIds: settings.placeholderIds || '',
|
|
16
|
+
intervalPosts: parseInt(settings.intervalPosts, 10) || 5,
|
|
17
|
+
enableMessageAds: settings.enableMessageAds === 'on',
|
|
18
|
+
showFirstMessageAd: settings.showFirstMessageAd === 'on',
|
|
19
|
+
messagePlaceholderIds: settings.messagePlaceholderIds || '',
|
|
20
|
+
messageIntervalPosts: parseInt(settings.messageIntervalPosts, 10) || 5,
|
|
21
|
+
excludedGroups: settings.excludedGroups || [],
|
|
22
|
+
};
|
|
72
23
|
}
|
|
73
24
|
|
|
74
|
-
plugin.onSettingsSet = function (data) {
|
|
75
|
-
// Invalider le cache dès que les settings de ce plugin sont sauvegardés via l'ACP
|
|
76
|
-
if (data && data.hash === SETTINGS_KEY) {
|
|
77
|
-
_settingsCache = null;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
plugin.addAdminNavigation = async (header) => {
|
|
82
|
-
header.plugins = header.plugins || [];
|
|
83
|
-
header.plugins.push({
|
|
84
|
-
route: '/plugins/ezoic-infinite',
|
|
85
|
-
icon: 'fa-ad',
|
|
86
|
-
name: 'Ezoic Infinite Ads'
|
|
87
|
-
});
|
|
88
|
-
return header;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
25
|
plugin.init = async ({ router, middleware }) => {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
26
|
+
const render = async (req, res) => {
|
|
27
|
+
const settings = await getSettings();
|
|
28
|
+
const allGroups = await getAllGroups(); // Utilise votre fonction existante
|
|
29
|
+
res.render('admin/plugins/ezoic-infinite', { ...settings, allGroups });
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, render);
|
|
33
|
+
router.get('/api/admin/plugins/ezoic-infinite', render);
|
|
34
|
+
|
|
35
|
+
// L'API que le client.js appelle
|
|
36
|
+
router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
|
|
37
|
+
const settings = await getSettings();
|
|
38
|
+
let excluded = false;
|
|
39
|
+
|
|
40
|
+
if (req.uid && settings.excludedGroups.length) {
|
|
41
|
+
excluded = await groups.isMemberOfAny(req.uid, settings.excludedGroups);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
res.json({
|
|
45
|
+
...settings,
|
|
46
|
+
excluded
|
|
47
|
+
});
|
|
102
48
|
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, render);
|
|
106
|
-
router.get('/api/admin/plugins/ezoic-infinite', render);
|
|
107
|
-
|
|
108
|
-
router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
|
|
109
|
-
const settings = await getSettings();
|
|
110
|
-
const excluded = await isUserExcluded(req.uid, settings.excludedGroups);
|
|
111
|
-
|
|
112
|
-
res.json({
|
|
113
|
-
excluded,
|
|
114
|
-
enableBetweenAds: settings.enableBetweenAds,
|
|
115
|
-
showFirstTopicAd: settings.showFirstTopicAd,
|
|
116
|
-
placeholderIds: settings.placeholderIds,
|
|
117
|
-
intervalPosts: settings.intervalPosts,
|
|
118
|
-
enableCategoryAds: settings.enableCategoryAds,
|
|
119
|
-
showFirstCategoryAd: settings.showFirstCategoryAd,
|
|
120
|
-
categoryPlaceholderIds: settings.categoryPlaceholderIds,
|
|
121
|
-
intervalCategories: settings.intervalCategories,
|
|
122
|
-
enableMessageAds: settings.enableMessageAds,
|
|
123
|
-
showFirstMessageAd: settings.showFirstMessageAd,
|
|
124
|
-
messagePlaceholderIds: settings.messagePlaceholderIds,
|
|
125
|
-
messageIntervalPosts: settings.messageIntervalPosts,
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
49
|
};
|
|
129
50
|
|
|
130
|
-
|
|
51
|
+
// ... gardez vos fonctions getAllGroups et addAdminNavigation ...
|
|
52
|
+
|
|
53
|
+
module.exports = plugin;
|
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,25 +1,50 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
// --- 1. VARIABLES ET CONFIG ---
|
|
5
4
|
const WRAP_CLASS = 'nodebb-ezoic-wrap';
|
|
6
5
|
const PINNED_ATTR = 'data-ezoic-pinned';
|
|
7
6
|
const PLACEHOLDER_PREFIX = 'ezoic-pub-ad-placeholder-';
|
|
8
7
|
|
|
9
8
|
let config = null;
|
|
10
9
|
let usedIds = new Set();
|
|
10
|
+
let isEzoicEnabled = false; // Flag crucial pour corriger vos logs d'erreur
|
|
11
11
|
let scheduleTimer = null;
|
|
12
12
|
|
|
13
|
-
// ---
|
|
13
|
+
// --- SÉLECTEURS ---
|
|
14
14
|
function getTopicList() {
|
|
15
|
-
return document.querySelector('
|
|
15
|
+
return document.querySelector('[component="topic/list"], [component="category"], [component="categories"]');
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function getPostList() {
|
|
19
19
|
return document.querySelector('[component="topic"], [component="post/list"]');
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
// ---
|
|
22
|
+
// --- LOGIQUE EZOIC ---
|
|
23
|
+
function callEzoic(idsToDefine) {
|
|
24
|
+
if (!window.ezstandalone || !idsToDefine.length) return;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// 1. Définir les placeholders
|
|
28
|
+
window.ezstandalone.define(idsToDefine);
|
|
29
|
+
|
|
30
|
+
// 2. Logique Enable vs Refresh (Correction de vos erreurs log)
|
|
31
|
+
if (!isEzoicEnabled && !window.ezstandalone.enabled) {
|
|
32
|
+
window.ezstandalone.enable();
|
|
33
|
+
window.ezstandalone.display();
|
|
34
|
+
isEzoicEnabled = true;
|
|
35
|
+
console.log('[Ezoic] First Enable & Display');
|
|
36
|
+
} else {
|
|
37
|
+
// On attend un cycle pour s'assurer qu'Ezoic est prêt pour le refresh
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
window.ezstandalone.refresh();
|
|
40
|
+
console.log('[Ezoic] Refreshing existing instance');
|
|
41
|
+
}, 100);
|
|
42
|
+
}
|
|
43
|
+
} catch (e) {
|
|
44
|
+
console.warn('[Ezoic] SDK Error:', e);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
23
48
|
function getNextId(poolStr) {
|
|
24
49
|
if (!poolStr) return null;
|
|
25
50
|
const ids = poolStr.split(/[\s,]+/).filter(Boolean);
|
|
@@ -33,131 +58,93 @@
|
|
|
33
58
|
return null;
|
|
34
59
|
}
|
|
35
60
|
|
|
36
|
-
// ---
|
|
37
|
-
function callEzoic(id) {
|
|
38
|
-
if (window.ezstandalone) {
|
|
39
|
-
try {
|
|
40
|
-
const nid = parseInt(id, 10);
|
|
41
|
-
window.ezstandalone.define(nid);
|
|
42
|
-
if (!window.ezstandalone.enabled) {
|
|
43
|
-
window.ezstandalone.enable();
|
|
44
|
-
window.ezstandalone.display();
|
|
45
|
-
} else {
|
|
46
|
-
window.ezstandalone.refresh();
|
|
47
|
-
}
|
|
48
|
-
} catch (e) { console.warn('[ezoic] refresh error', id, e); }
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// --- 5. INJECTION SÉCURISÉE (ANTI-PILEUP) ---
|
|
61
|
+
// --- INJECTION ---
|
|
53
62
|
function inject() {
|
|
54
63
|
if (!config || config.excluded) return;
|
|
64
|
+
const newIds = [];
|
|
55
65
|
|
|
56
|
-
// A.
|
|
66
|
+
// A. Topics / Categories
|
|
57
67
|
const ul = getTopicList();
|
|
58
68
|
if (ul && config.enableBetweenAds) {
|
|
59
|
-
// Nettoyage
|
|
60
|
-
|
|
61
|
-
wraps.forEach(w => {
|
|
69
|
+
// Nettoyage anti-pileup (remontée)
|
|
70
|
+
ul.querySelectorAll('.' + WRAP_CLASS).forEach(w => {
|
|
62
71
|
if (!w.previousElementSibling || w.previousElementSibling.tagName !== 'LI') w.remove();
|
|
63
72
|
});
|
|
64
73
|
|
|
65
74
|
const items = Array.from(ul.children).filter(c => c.tagName === 'LI' && !c.classList.contains(WRAP_CLASS));
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
|
|
76
|
+
// First Ad
|
|
77
|
+
if (config.showFirstTopicAd && !ul.querySelector(`[${PINNED_ATTR}="true"]`)) {
|
|
78
|
+
const id = getNextId(config.placeholderIds);
|
|
79
|
+
if (id) {
|
|
80
|
+
insertAd(items[0], id, true);
|
|
81
|
+
newIds.push(parseInt(id, 10));
|
|
70
82
|
}
|
|
83
|
+
}
|
|
71
84
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
// Intervals
|
|
86
|
+
const interval = parseInt(config.intervalPosts, 10) || 5;
|
|
87
|
+
items.forEach((li, idx) => {
|
|
88
|
+
if ((idx + 1) % interval === 0 && idx > 0) {
|
|
89
|
+
if (!li.nextElementSibling || !li.nextElementSibling.classList.contains(WRAP_CLASS)) {
|
|
90
|
+
const id = getNextId(config.placeholderIds);
|
|
91
|
+
if (id) {
|
|
92
|
+
insertAd(li, id, false);
|
|
93
|
+
newIds.push(parseInt(id, 10));
|
|
81
94
|
}
|
|
82
95
|
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
96
|
+
}
|
|
97
|
+
});
|
|
85
98
|
}
|
|
86
99
|
|
|
87
|
-
// B.
|
|
100
|
+
// B. Messages (Posts)
|
|
88
101
|
const postList = getPostList();
|
|
89
102
|
if (postList && config.enableMessageAds) {
|
|
90
103
|
const posts = Array.from(postList.querySelectorAll('[component="post"]'));
|
|
91
104
|
if (config.showFirstMessageAd && posts.length > 0 && !postList.querySelector('.ezoic-msg-first')) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
posts.forEach((post, idx) => {
|
|
98
|
-
const pPos = idx + 1;
|
|
99
|
-
if (pPos > 1 && pPos % msgInterval === 0) {
|
|
100
|
-
if (!post.nextElementSibling || !post.nextElementSibling.classList.contains(WRAP_CLASS)) {
|
|
101
|
-
insertAd(post, getNextId(config.messagePlaceholderIds), false);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
+
const id = getNextId(config.messagePlaceholderIds);
|
|
106
|
+
if (id) {
|
|
107
|
+
insertAd(posts[0], id, false, 'ezoic-msg-first');
|
|
108
|
+
newIds.push(parseInt(id, 10));
|
|
109
|
+
}
|
|
105
110
|
}
|
|
106
111
|
}
|
|
112
|
+
|
|
113
|
+
if (newIds.length > 0) {
|
|
114
|
+
callEzoic(newIds);
|
|
115
|
+
}
|
|
107
116
|
}
|
|
108
117
|
|
|
109
118
|
function insertAd(target, id, isPinned, extraClass = '') {
|
|
110
|
-
if (!target || !id) return;
|
|
111
119
|
const wrap = document.createElement('div');
|
|
112
120
|
wrap.className = `${WRAP_CLASS} ezoic-ad-between ${extraClass}`;
|
|
113
121
|
if (isPinned) wrap.setAttribute(PINNED_ATTR, 'true');
|
|
114
|
-
|
|
115
|
-
const placeholder = document.createElement('div');
|
|
116
|
-
placeholder.id = PLACEHOLDER_PREFIX + id;
|
|
117
|
-
wrap.appendChild(placeholder);
|
|
118
|
-
|
|
122
|
+
wrap.innerHTML = `<div id="${PLACEHOLDER_PREFIX}${id}"></div>`;
|
|
119
123
|
target.after(wrap);
|
|
120
|
-
|
|
121
|
-
// On laisse le DOM respirer 50ms avant d'appeler Ezoic
|
|
122
|
-
setTimeout(() => callEzoic(id), 50);
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
// ---
|
|
126
|
-
async function
|
|
126
|
+
// --- INIT ---
|
|
127
|
+
async function init() {
|
|
127
128
|
try {
|
|
128
129
|
const res = await fetch('/api/plugins/ezoic-infinite/config');
|
|
129
130
|
config = await res.json();
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
function schedule() {
|
|
134
|
-
if (scheduleTimer) clearTimeout(scheduleTimer);
|
|
135
|
-
scheduleTimer = setTimeout(inject, 250);
|
|
136
|
-
}
|
|
131
|
+
|
|
132
|
+
inject();
|
|
137
133
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// MutationObserver pour le scroll infini
|
|
143
|
-
const body = document.body;
|
|
144
|
-
const mo = new MutationObserver((mutations) => {
|
|
145
|
-
for (let m of mutations) {
|
|
146
|
-
if (m.addedNodes.length > 0) {
|
|
147
|
-
schedule();
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
134
|
+
const observer = new MutationObserver((mutations) => {
|
|
135
|
+
if (mutations.some(m => m.addedNodes.length > 0)) {
|
|
136
|
+
if (scheduleTimer) clearTimeout(scheduleTimer);
|
|
137
|
+
scheduleTimer = setTimeout(inject, 300);
|
|
150
138
|
}
|
|
151
139
|
});
|
|
152
|
-
|
|
153
|
-
});
|
|
140
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
154
141
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
}
|
|
142
|
+
if (window.jQuery) {
|
|
143
|
+
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', () => {
|
|
144
|
+
setTimeout(inject, 500);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
} catch (e) {}
|
|
161
148
|
}
|
|
162
149
|
|
|
163
150
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
package/public/style.css
CHANGED
|
@@ -1,100 +1,80 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Keep our NodeBB-inserted wrappers CLS-safe.
|
|
3
|
-
NOTE: must not rely on `.ezoic-ad` because Ezoic uses that class internally.
|
|
4
|
-
*/
|
|
1
|
+
/* --- Conteneur principal inséré entre les sujets/messages --- */
|
|
5
2
|
.nodebb-ezoic-wrap {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
display: block !important;
|
|
4
|
+
width: 100% !important;
|
|
5
|
+
clear: both !important;
|
|
6
|
+
margin: 20px 0 !important;
|
|
7
|
+
padding: 0 !important;
|
|
8
|
+
/* Important : Empêche les éléments de déborder lors du recyclage du DOM */
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
/* Améliore les performances de rendu sur NodeBB 4.x */
|
|
11
|
+
contain: layout style;
|
|
12
|
+
/* Assure une visibilité minimale pour que le SDK Ezoic puisse mesurer l'emplacement */
|
|
13
|
+
min-height: 50px;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
/* --- Protection spécifique pour la 1ère publicité (Pinned) --- */
|
|
17
|
+
.nodebb-ezoic-wrap[data-ezoic-pinned="true"] {
|
|
18
|
+
/* Réserve une hauteur pour éviter le "Layout Shift" (saut d'écran) au scroll up */
|
|
19
|
+
min-height: 150px;
|
|
20
|
+
border-bottom: 1px solid rgba(0,0,0,0.05);
|
|
21
|
+
margin-top: 5px !important;
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
/*
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
/* --- Le placeholder Ezoic interne --- */
|
|
25
|
+
[id^="ezoic-pub-ad-placeholder-"] {
|
|
26
|
+
margin: 0 auto !important;
|
|
27
|
+
padding: 0 !important;
|
|
28
|
+
text-align: center;
|
|
29
|
+
min-height: 1px;
|
|
30
|
+
line-height: 0;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
/*
|
|
28
|
-
.nodebb-ezoic-wrap,
|
|
29
|
-
.nodebb-ezoic-wrap
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
/* --- Forcer le centrage et la réactivité des pubs Ezoic --- */
|
|
34
|
+
.nodebb-ezoic-wrap span.ezoic-ad,
|
|
35
|
+
.nodebb-ezoic-wrap .ezoic-ad,
|
|
36
|
+
.nodebb-ezoic-wrap iframe {
|
|
37
|
+
display: block !important;
|
|
38
|
+
margin: 0 auto !important;
|
|
39
|
+
max-width: 100% !important;
|
|
32
40
|
}
|
|
33
41
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
/* --- Gestion des blocs vides (Anti-Trous blancs) --- */
|
|
43
|
+
/* Si Ezoic ne remplit pas l'emplacement ou si le script le vide,
|
|
44
|
+
on réduit l'espace pour ne pas casser le design du forum */
|
|
45
|
+
.nodebb-ezoic-wrap:empty {
|
|
46
|
+
height: 0 !important;
|
|
47
|
+
min-height: 0 !important;
|
|
48
|
+
margin: 0 !important;
|
|
49
|
+
border: none !important;
|
|
38
50
|
}
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
/* --- Adaptation pour les messages (Topic View) --- */
|
|
53
|
+
.ezoic-msg-first {
|
|
54
|
+
margin-bottom: 35px !important;
|
|
55
|
+
padding-bottom: 15px !important;
|
|
56
|
+
border-bottom: 1px solid var(--border-color, #eee);
|
|
43
57
|
}
|
|
44
58
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
display: block !important;
|
|
49
|
-
margin: 0 !important;
|
|
50
|
-
padding: 0 !important;
|
|
51
|
-
/* Don't fully collapse (can prevent fill / triggers "unused"), keep it at 1px */
|
|
52
|
-
height: 1px !important;
|
|
53
|
-
min-height: 1px !important;
|
|
54
|
-
overflow: hidden !important;
|
|
59
|
+
/* --- Correction pour le mode sombre (NodeBB 4 Harmony) --- */
|
|
60
|
+
[data-theme="dark"] .nodebb-ezoic-wrap[data-ezoic-pinned="true"] {
|
|
61
|
+
border-bottom-color: rgba(255,255,255,0.1);
|
|
55
62
|
}
|
|
56
63
|
|
|
57
|
-
/*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.nodebb-ezoic-wrap .ezoic-ad,
|
|
67
|
-
.nodebb-ezoic-wrap span.ezoic-ad {
|
|
68
|
-
min-height: 1px !important; /* kill 400px gaps */
|
|
69
|
-
height: auto !important;
|
|
64
|
+
/* --- Neutralisation des marges Ezoic sur mobile --- */
|
|
65
|
+
@media (max-width: 767px) {
|
|
66
|
+
.nodebb-ezoic-wrap {
|
|
67
|
+
margin: 10px 0 !important;
|
|
68
|
+
min-height: 30px;
|
|
69
|
+
}
|
|
70
|
+
.nodebb-ezoic-wrap[data-ezoic-pinned="true"] {
|
|
71
|
+
min-height: 100px;
|
|
72
|
+
}
|
|
70
73
|
}
|
|
71
74
|
|
|
72
|
-
/*
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
/* Ezoic sometimes injects `position: sticky` inside placements. In long NodeBB topics,
|
|
76
|
-
this can create "gliding" and sudden disappear/reappear effects while scrolling.
|
|
77
|
-
We neutralize sticky positioning *inside our injected wrappers* only. */
|
|
75
|
+
/* --- Sécurité Anti-Sticky ---
|
|
76
|
+
Ezoic injecte parfois du 'position: sticky' qui fait "flotter" les pubs
|
|
77
|
+
de manière erratique lors du scroll infini. On le neutralise ici. */
|
|
78
78
|
.nodebb-ezoic-wrap .ezads-sticky-intradiv {
|
|
79
|
-
|
|
80
|
-
top: auto !important;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
/* ===== V17 host styling ===== */
|
|
85
|
-
li.nodebb-ezoic-host { list-style: none; width: 100%; display: block; }
|
|
86
|
-
li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width: 100%; display: block; }
|
|
87
|
-
/* ===== /V17 ===== */
|
|
88
|
-
|
|
89
|
-
.nodebb-ezoic-wrap {
|
|
90
|
-
display: block;
|
|
91
|
-
width: 100%;
|
|
92
|
-
clear: both;
|
|
93
|
-
margin: 20px 0 !important; /* Ajoute un peu d'espace pour le visuel */
|
|
94
|
-
min-height: 50px; /* Évite que le bloc ne soit écrasé à 0px */
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/* Si la pub est vide, on garde un petit espace pour éviter le saut au scroll up */
|
|
98
|
-
.nodebb-ezoic-wrap:empty {
|
|
99
|
-
min-height: 1px;
|
|
79
|
+
position: static !important;
|
|
100
80
|
}
|