nodebb-plugin-ezoic-infinite 1.6.54 → 1.6.56
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 +99 -60
- package/public/style.css +51 -84
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,32 +1,39 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
// --- 1. VARIABLES ET CONFIG ---
|
|
4
5
|
const WRAP_CLASS = 'nodebb-ezoic-wrap';
|
|
5
6
|
const PINNED_ATTR = 'data-ezoic-pinned';
|
|
6
|
-
const PROCESSED_ATTR = 'data-ezoic-seen'; // Nouveau : marqueur de sujet
|
|
7
7
|
const PLACEHOLDER_PREFIX = 'ezoic-pub-ad-placeholder-';
|
|
8
|
-
|
|
8
|
+
|
|
9
9
|
let config = null;
|
|
10
10
|
let usedIds = new Set();
|
|
11
11
|
let scheduleTimer = null;
|
|
12
12
|
|
|
13
|
+
// --- 2. SÉLECTEURS DE THÈMES ---
|
|
13
14
|
function getTopicList() {
|
|
14
|
-
return document.querySelector('[component="
|
|
15
|
+
return document.querySelector('.category [component="category"], .categories [component="categories"], [component="topic/list"], .topic-list');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getPostList() {
|
|
19
|
+
return document.querySelector('[component="topic"], [component="post/list"]');
|
|
15
20
|
}
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
// --- 3. LOGIQUE DES POOLS D'ID ---
|
|
23
|
+
function getNextId(poolStr) {
|
|
24
|
+
if (!poolStr) return null;
|
|
25
|
+
const ids = poolStr.split(/[\s,]+/).filter(Boolean);
|
|
20
26
|
for (const id of ids) {
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
23
|
-
usedIds.add(
|
|
24
|
-
return
|
|
27
|
+
const num = id.replace(/[^\d]/g, '');
|
|
28
|
+
if (num && !usedIds.has(num)) {
|
|
29
|
+
usedIds.add(num);
|
|
30
|
+
return num;
|
|
25
31
|
}
|
|
26
32
|
}
|
|
27
33
|
return null;
|
|
28
34
|
}
|
|
29
35
|
|
|
36
|
+
// --- 4. APPEL EZOIC (REFRESH & SHOW) ---
|
|
30
37
|
function callEzoic(id) {
|
|
31
38
|
if (window.ezstandalone) {
|
|
32
39
|
try {
|
|
@@ -38,87 +45,119 @@
|
|
|
38
45
|
} else {
|
|
39
46
|
window.ezstandalone.refresh();
|
|
40
47
|
}
|
|
41
|
-
} catch (e) { console.warn('[ezoic]
|
|
48
|
+
} catch (e) { console.warn('[ezoic] refresh error', id, e); }
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
51
|
|
|
52
|
+
// --- 5. INJECTION SÉCURISÉE (ANTI-PILEUP) ---
|
|
45
53
|
function inject() {
|
|
46
54
|
if (!config || config.excluded) return;
|
|
47
|
-
const ul = getTopicList();
|
|
48
|
-
if (!ul) return;
|
|
49
55
|
|
|
50
|
-
//
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
56
|
+
// A. LISTE DES SUJETS (Topics)
|
|
57
|
+
const ul = getTopicList();
|
|
58
|
+
if (ul && config.enableBetweenAds) {
|
|
59
|
+
// Nettoyage des pubs orphelines (celles qui n'ont plus de LI au dessus à cause du scroll up)
|
|
60
|
+
const wraps = ul.querySelectorAll('.' + WRAP_CLASS);
|
|
61
|
+
wraps.forEach(w => {
|
|
62
|
+
if (!w.previousElementSibling || w.previousElementSibling.tagName !== 'LI') w.remove();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const items = Array.from(ul.children).filter(c => c.tagName === 'LI' && !c.classList.contains(WRAP_CLASS));
|
|
66
|
+
if (items.length > 0) {
|
|
67
|
+
// Pub n°1 (Pinned)
|
|
68
|
+
if (config.showFirstTopicAd && !ul.querySelector(`[${PINNED_ATTR}="true"]`)) {
|
|
69
|
+
insertAd(items[0], getNextId(config.placeholderIds), true);
|
|
70
|
+
}
|
|
53
71
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
72
|
+
// Pubs d'intervalles
|
|
73
|
+
const interval = parseInt(config.intervalPosts, 10) || 5;
|
|
74
|
+
items.forEach((li, idx) => {
|
|
75
|
+
const pos = idx + 1;
|
|
76
|
+
if (pos > 1 && pos % interval === 0) {
|
|
77
|
+
// Vérifie si ce LI a déjà une pub juste après lui pour éviter les doublons
|
|
78
|
+
const next = li.nextElementSibling;
|
|
79
|
+
if (!next || !next.classList.contains(WRAP_CLASS)) {
|
|
80
|
+
insertAd(li, getNextId(config.placeholderIds), false);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
58
85
|
}
|
|
59
86
|
|
|
60
|
-
//
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
87
|
+
// B. LISTE DES MESSAGES (Posts)
|
|
88
|
+
const postList = getPostList();
|
|
89
|
+
if (postList && config.enableMessageAds) {
|
|
90
|
+
const posts = Array.from(postList.querySelectorAll('[component="post"]'));
|
|
91
|
+
if (config.showFirstMessageAd && posts.length > 0 && !postList.querySelector('.ezoic-msg-first')) {
|
|
92
|
+
insertAd(posts[0], getNextId(config.messagePlaceholderIds), false, 'ezoic-msg-first');
|
|
93
|
+
}
|
|
66
94
|
|
|
67
|
-
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
95
|
+
const msgInterval = parseInt(config.messageIntervalPosts, 10) || 5;
|
|
96
|
+
if (msgInterval > 0) {
|
|
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
|
+
});
|
|
75
105
|
}
|
|
76
|
-
}
|
|
106
|
+
}
|
|
77
107
|
}
|
|
78
108
|
|
|
79
|
-
function insertAd(target, id, isPinned) {
|
|
80
|
-
if (!
|
|
81
|
-
|
|
109
|
+
function insertAd(target, id, isPinned, extraClass = '') {
|
|
110
|
+
if (!target || !id) return;
|
|
82
111
|
const wrap = document.createElement('div');
|
|
83
|
-
wrap.className = WRAP_CLASS
|
|
112
|
+
wrap.className = `${WRAP_CLASS} ezoic-ad-between ${extraClass}`;
|
|
84
113
|
if (isPinned) wrap.setAttribute(PINNED_ATTR, 'true');
|
|
85
114
|
|
|
86
115
|
const placeholder = document.createElement('div');
|
|
87
116
|
placeholder.id = PLACEHOLDER_PREFIX + id;
|
|
88
117
|
wrap.appendChild(placeholder);
|
|
89
118
|
|
|
90
|
-
// Insertion physique
|
|
91
119
|
target.after(wrap);
|
|
92
120
|
|
|
93
|
-
// On
|
|
121
|
+
// On laisse le DOM respirer 50ms avant d'appeler Ezoic
|
|
94
122
|
setTimeout(() => callEzoic(id), 50);
|
|
95
123
|
}
|
|
96
124
|
|
|
97
|
-
|
|
125
|
+
// --- 6. INITIALISATION ET HOOKS ---
|
|
126
|
+
async function fetchConfig() {
|
|
98
127
|
try {
|
|
99
128
|
const res = await fetch('/api/plugins/ezoic-infinite/config');
|
|
100
129
|
config = await res.json();
|
|
130
|
+
} catch (e) { console.error('[ezoic] config fetch failed'); }
|
|
131
|
+
}
|
|
101
132
|
|
|
102
|
-
|
|
103
|
-
|
|
133
|
+
function schedule() {
|
|
134
|
+
if (scheduleTimer) clearTimeout(scheduleTimer);
|
|
135
|
+
scheduleTimer = setTimeout(inject, 250);
|
|
136
|
+
}
|
|
104
137
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
138
|
+
function init() {
|
|
139
|
+
fetchConfig().then(() => {
|
|
140
|
+
schedule();
|
|
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
|
+
}
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
mo.observe(body, { childList: true, subtree: true });
|
|
153
|
+
});
|
|
114
154
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
} catch (e) { console.error('[ezoic] init fail', e); }
|
|
155
|
+
// Compatibilité jQuery/NodeBB Events
|
|
156
|
+
if (window.jQuery) {
|
|
157
|
+
window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', () => {
|
|
158
|
+
setTimeout(schedule, 500);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
122
161
|
}
|
|
123
162
|
|
|
124
163
|
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
|
package/public/style.css
CHANGED
|
@@ -1,100 +1,67 @@
|
|
|
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 de la publicité --- */
|
|
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
|
+
/* Empêche les débordements visuels lors du recyclage du DOM */
|
|
9
|
+
overflow: hidden;
|
|
10
|
+
/* Améliore les performances de rendu sur NodeBB 4.x */
|
|
11
|
+
contain: layout style;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
/* --- Protection du premier emplacement (Pinned) --- */
|
|
15
|
+
.nodebb-ezoic-wrap[data-ezoic-pinned="true"] {
|
|
16
|
+
/* On réserve une hauteur minimale pour éviter que le contenu
|
|
17
|
+
ne saute violemment quand la pub charge au scroll up */
|
|
18
|
+
min-height: 120px;
|
|
19
|
+
border-bottom: 1px solid rgba(0,0,0,0.05);
|
|
20
|
+
margin-top: 0 !important;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
/*
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
/* --- Le placeholder Ezoic interne --- */
|
|
24
|
+
[id^="ezoic-pub-ad-placeholder-"] {
|
|
25
|
+
margin: 0 auto !important;
|
|
26
|
+
padding: 0 !important;
|
|
27
|
+
text-align: center;
|
|
28
|
+
min-height: 1px;
|
|
29
|
+
line-height: 0;
|
|
25
30
|
}
|
|
26
31
|
|
|
27
|
-
/*
|
|
28
|
-
.nodebb-ezoic-wrap,
|
|
29
|
-
.nodebb-ezoic-wrap
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
.nodebb-ezoic-wrap iframe,
|
|
35
|
-
.nodebb-ezoic-wrap div[id$="__container__"] iframe {
|
|
36
|
-
display: block !important;
|
|
37
|
-
vertical-align: top !important;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
.nodebb-ezoic-wrap div[id$="__container__"] {
|
|
41
|
-
display: block !important;
|
|
42
|
-
line-height: 0 !important;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
/* Collapse empty ad blocks (prevents "holes" when an ad doesn't fill or gets destroyed) */
|
|
47
|
-
.nodebb-ezoic-wrap.is-empty {
|
|
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;
|
|
32
|
+
/* --- Nettoyage des éléments Ezoic internes --- */
|
|
33
|
+
.nodebb-ezoic-wrap span.ezoic-ad,
|
|
34
|
+
.nodebb-ezoic-wrap .ezoic-ad {
|
|
35
|
+
display: block !important;
|
|
36
|
+
margin: 0 auto !important;
|
|
37
|
+
/* Force Ezoic à respecter la largeur du flux forum */
|
|
38
|
+
max-width: 100% !important;
|
|
55
39
|
}
|
|
56
40
|
|
|
57
|
-
/*
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
/* Remove Ezoic's large reserved min-height inside our wrappers (topics/messages) */
|
|
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;
|
|
41
|
+
/* --- Gestion des blocs vides (Anti-Holes) --- */
|
|
42
|
+
/* Si Ezoic ne renvoie rien ou si le bloc est vide,
|
|
43
|
+
on réduit l'espace pour ne pas avoir de gros trous blancs */
|
|
44
|
+
.nodebb-ezoic-wrap:empty {
|
|
45
|
+
height: 0 !important;
|
|
46
|
+
min-height: 0 !important;
|
|
47
|
+
margin: 0 !important;
|
|
70
48
|
}
|
|
71
49
|
|
|
72
|
-
/*
|
|
73
|
-
.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
We neutralize sticky positioning *inside our injected wrappers* only. */
|
|
78
|
-
.nodebb-ezoic-wrap .ezads-sticky-intradiv {
|
|
79
|
-
position: static !important;
|
|
80
|
-
top: auto !important;
|
|
50
|
+
/* --- Adaptation spécifique pour les Messages (Sujets ouverts) --- */
|
|
51
|
+
.ezoic-msg-first {
|
|
52
|
+
margin-bottom: 30px !important;
|
|
53
|
+
border-bottom: 1px solid var(--border-color, #eee);
|
|
54
|
+
padding-bottom: 10px !important;
|
|
81
55
|
}
|
|
82
56
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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 */
|
|
57
|
+
/* --- Support du mode sombre (NodeBB 4 Harmony) --- */
|
|
58
|
+
[data-theme="dark"] .nodebb-ezoic-wrap[data-ezoic-pinned="true"] {
|
|
59
|
+
border-bottom-color: rgba(255,255,255,0.1);
|
|
95
60
|
}
|
|
96
61
|
|
|
97
|
-
/*
|
|
98
|
-
|
|
99
|
-
|
|
62
|
+
/* --- Neutralisation des marges forcées par Ezoic sur mobile --- */
|
|
63
|
+
@media (max-width: 767px) {
|
|
64
|
+
.nodebb-ezoic-wrap {
|
|
65
|
+
margin: 10px 0 !important;
|
|
66
|
+
}
|
|
100
67
|
}
|