nodebb-plugin-ezoic-infinite 1.6.81 → 1.6.83
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 +40 -48
- package/package.json +1 -1
- package/public/client.js +105 -223
- package/public/style.css +13 -28
- package/public/test.txt +0 -1
package/library.js
CHANGED
|
@@ -2,66 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
const meta = require.main.require('./src/meta');
|
|
4
4
|
const groups = require.main.require('./src/groups');
|
|
5
|
-
const db = require.main.require('./src/database');
|
|
6
5
|
|
|
7
6
|
const SETTINGS_KEY = 'ezoic-infinite';
|
|
8
7
|
const plugin = {};
|
|
9
8
|
|
|
10
9
|
async function getSettings() {
|
|
11
|
-
|
|
12
|
-
return settings || {};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async function isUserExcluded(uid, excludedGroups) {
|
|
16
|
-
if (!uid || !excludedGroups) return false;
|
|
17
|
-
const groupsList = Array.isArray(excludedGroups) ? excludedGroups : [excludedGroups];
|
|
18
|
-
if (!groupsList.length) return false;
|
|
19
|
-
return await groups.isMemberOfGroups(uid, groupsList);
|
|
10
|
+
return await meta.settings.get(SETTINGS_KEY) || {};
|
|
20
11
|
}
|
|
21
12
|
|
|
22
13
|
plugin.init = async ({ router, middleware }) => {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
14
|
+
const renderAdmin = async (req, res) => {
|
|
15
|
+
const settings = await getSettings();
|
|
16
|
+
const db = require.main.require('./src/database');
|
|
17
|
+
const names = await db.getSortedSetRange('groups:createtime', 0, -1);
|
|
18
|
+
const groupsData = await groups.getGroupsData(names);
|
|
19
|
+
const allGroups = groupsData.filter(g => g && g.name).map(g => ({ name: g.name }));
|
|
20
|
+
|
|
21
|
+
res.render('admin/plugins/ezoic-infinite', {
|
|
22
|
+
...settings,
|
|
23
|
+
allGroups,
|
|
24
|
+
enableBetweenAds_checked: settings.enableBetweenAds === 'on' ? 'checked' : '',
|
|
25
|
+
enableMessageAds_checked: settings.enableMessageAds === 'on' ? 'checked' : ''
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, renderAdmin);
|
|
30
|
+
router.get('/api/admin/plugins/ezoic-infinite', renderAdmin);
|
|
31
|
+
|
|
32
|
+
router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
|
|
33
|
+
const settings = await getSettings();
|
|
34
|
+
let excluded = false;
|
|
35
|
+
if (req.uid && settings.excludedGroups) {
|
|
36
|
+
const groupsList = Array.isArray(settings.excludedGroups) ? settings.excludedGroups : [settings.excludedGroups];
|
|
37
|
+
excluded = await groups.isMemberOfGroups(req.uid, groupsList);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
res.json({
|
|
41
|
+
excluded,
|
|
42
|
+
enableBetweenAds: settings.enableBetweenAds === 'on',
|
|
43
|
+
showFirstTopicAd: settings.showFirstTopicAd === 'on',
|
|
44
|
+
placeholderIds: settings.placeholderIds || '',
|
|
45
|
+
intervalPosts: settings.intervalPosts || 10,
|
|
46
|
+
enableMessageAds: settings.enableMessageAds === 'on',
|
|
47
|
+
showFirstMessageAd: settings.showFirstMessageAd === 'on',
|
|
48
|
+
messagePlaceholderIds: settings.messagePlaceholderIds || '',
|
|
49
|
+
messageIntervalPosts: settings.messageIntervalPosts || 10,
|
|
50
|
+
});
|
|
54
51
|
});
|
|
55
|
-
});
|
|
56
52
|
};
|
|
57
53
|
|
|
58
54
|
plugin.addAdminNavigation = async (header) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
icon: 'fa-ad',
|
|
62
|
-
name: 'Ezoic Infinite'
|
|
63
|
-
});
|
|
64
|
-
return header;
|
|
55
|
+
header.plugins.push({ route: '/plugins/ezoic-infinite', icon: 'fa-ad', name: 'Ezoic Infinite' });
|
|
56
|
+
return header;
|
|
65
57
|
};
|
|
66
58
|
|
|
67
59
|
module.exports = plugin;
|
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -1,237 +1,119 @@
|
|
|
1
1
|
(function () {
|
|
2
|
-
|
|
2
|
+
'use strict';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
try {
|
|
7
|
-
lastScrollY = window.scrollY || 0;
|
|
8
|
-
window.addEventListener('scroll', () => {
|
|
9
|
-
const y = window.scrollY || 0;
|
|
10
|
-
const d = y - lastScrollY;
|
|
11
|
-
if (Math.abs(d) > 4) {
|
|
12
|
-
scrollDir = d > 0 ? 1 : -1;
|
|
13
|
-
lastScrollY = y;
|
|
14
|
-
}
|
|
15
|
-
}, { passive: true });
|
|
16
|
-
} catch (e) {}
|
|
4
|
+
if (window.ezInfiniteInjected) return;
|
|
5
|
+
window.ezInfiniteInjected = true;
|
|
17
6
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
let p = document.getElementById(POOL_ID);
|
|
33
|
-
if (!p) {
|
|
34
|
-
p = document.createElement('div');
|
|
35
|
-
p.id = POOL_ID;
|
|
36
|
-
p.style.display = 'none';
|
|
37
|
-
document.body.appendChild(p);
|
|
38
|
-
}
|
|
39
|
-
return p;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function releaseWrapNode(wrap) {
|
|
43
|
-
if (!wrap || !wrap.parentNode) return;
|
|
44
|
-
const pool = getPool();
|
|
45
|
-
wrap.classList.remove('ez-orphan-hidden');
|
|
46
|
-
if (wrap.parentNode !== pool) {
|
|
47
|
-
pool.appendChild(wrap);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Anti-pillup : évite que deux pubs se suivent
|
|
52
|
-
function decluster(container) {
|
|
53
|
-
const wraps = Array.from(container.querySelectorAll(`.${WRAP_CLASS}`));
|
|
54
|
-
wraps.forEach(wrap => {
|
|
55
|
-
let next = wrap.nextElementSibling;
|
|
56
|
-
if (next && next.classList.contains(WRAP_CLASS)) {
|
|
57
|
-
withInternalDomChange(() => releaseWrapNode(next));
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Nettoyage des pubs orphelines lors de la virtualisation
|
|
63
|
-
function pruneOrphanWraps() {
|
|
64
|
-
const wraps = document.querySelectorAll(`.${WRAP_CLASS}`);
|
|
65
|
-
const items = document.querySelectorAll('[component="category/topic"], [component="post"]');
|
|
66
|
-
const itemSet = new Set(items);
|
|
67
|
-
|
|
68
|
-
wraps.forEach(wrap => {
|
|
69
|
-
if (wrap.parentElement && wrap.parentElement.id === POOL_ID) return;
|
|
70
|
-
let hasNeighbor = false;
|
|
71
|
-
let prev = wrap.previousElementSibling;
|
|
72
|
-
for (let i = 0; i < 3 && prev; i++) {
|
|
73
|
-
if (itemSet.has(prev)) { hasNeighbor = true; break; }
|
|
74
|
-
prev = prev.previousElementSibling;
|
|
75
|
-
}
|
|
76
|
-
if (!hasNeighbor) {
|
|
77
|
-
let next = wrap.nextElementSibling;
|
|
78
|
-
for (let i = 0; i < 3 && next; i++) {
|
|
79
|
-
if (itemSet.has(next)) { hasNeighbor = true; break; }
|
|
80
|
-
next = next.nextElementSibling;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (!hasNeighbor) {
|
|
85
|
-
if (scrollDir === -1) {
|
|
86
|
-
withInternalDomChange(() => releaseWrapNode(wrap));
|
|
87
|
-
} else {
|
|
88
|
-
wrap.classList.add('ez-orphan-hidden');
|
|
7
|
+
const WRAP_CLASS = 'nodebb-ezoic-wrap';
|
|
8
|
+
const POOL_ID = 'nodebb-ezoic-placeholder-pool';
|
|
9
|
+
|
|
10
|
+
let config = null;
|
|
11
|
+
let isInternalChange = false;
|
|
12
|
+
let ezEnabled = false;
|
|
13
|
+
|
|
14
|
+
function getPool() {
|
|
15
|
+
let p = document.getElementById(POOL_ID);
|
|
16
|
+
if (!p) {
|
|
17
|
+
p = document.createElement('div');
|
|
18
|
+
p.id = POOL_ID;
|
|
19
|
+
p.style.display = 'none';
|
|
20
|
+
document.body.appendChild(p);
|
|
89
21
|
}
|
|
90
|
-
|
|
91
|
-
wrap.classList.remove('ez-orphan-hidden');
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function callEzoic(placeholderId) {
|
|
97
|
-
if (typeof window.ezstandalone === 'undefined') return;
|
|
98
|
-
const pid = parseInt(placeholderId, 10);
|
|
99
|
-
if (isNaN(pid)) return;
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
if (!ezInitialized) {
|
|
103
|
-
// PREMIER APPEL : define + enable + display
|
|
104
|
-
window.ezstandalone.define(pid);
|
|
105
|
-
window.ezstandalone.enable();
|
|
106
|
-
window.ezstandalone.display();
|
|
107
|
-
ezInitialized = true;
|
|
108
|
-
} else {
|
|
109
|
-
// APPELS SUIVANTS (Scroll) : define + refresh
|
|
110
|
-
window.ezstandalone.define(pid);
|
|
111
|
-
// On attend que le DOM soit stable pour le refresh
|
|
112
|
-
setTimeout(() => {
|
|
113
|
-
if (document.getElementById('ezoic-pub-ad-placeholder-' + pid)) {
|
|
114
|
-
window.ezstandalone.refresh();
|
|
115
|
-
}
|
|
116
|
-
}, 150);
|
|
117
|
-
}
|
|
118
|
-
} catch (e) {
|
|
119
|
-
console.warn('[Ezoic-Infinite] Ez Error:', e.message);
|
|
22
|
+
return p;
|
|
120
23
|
}
|
|
121
|
-
}
|
|
122
24
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
if (isTopicList && config.enableBetweenAds) {
|
|
139
|
-
enabled = true;
|
|
140
|
-
interval = parseInt(config.intervalPosts, 10) || 10;
|
|
141
|
-
kind = 'between';
|
|
142
|
-
items = Array.from(container.querySelectorAll('[component="category/topic"]'));
|
|
143
|
-
showFirst = config.showFirstTopicAd;
|
|
144
|
-
} else if (isPostList && config.enableMessageAds) {
|
|
145
|
-
enabled = true;
|
|
146
|
-
interval = parseInt(config.messageIntervalPosts, 10) || 10;
|
|
147
|
-
kind = 'message';
|
|
148
|
-
items = Array.from(container.querySelectorAll('[component="post"]'));
|
|
149
|
-
showFirst = config.showFirstMessageAd;
|
|
25
|
+
function callEzoic(pid) {
|
|
26
|
+
if (typeof window.ezstandalone === 'undefined') return;
|
|
27
|
+
const id = parseInt(pid, 10);
|
|
28
|
+
try {
|
|
29
|
+
window.ezstandalone.define(id);
|
|
30
|
+
if (!ezEnabled) {
|
|
31
|
+
window.ezstandalone.enable();
|
|
32
|
+
window.ezstandalone.display();
|
|
33
|
+
ezEnabled = true;
|
|
34
|
+
} else {
|
|
35
|
+
// Délai pour s'assurer que le div est bien rendu par le navigateur
|
|
36
|
+
setTimeout(() => { window.ezstandalone.refresh(); }, 200);
|
|
37
|
+
}
|
|
38
|
+
} catch (e) { console.warn('[Ezoic] Error:', e); }
|
|
150
39
|
}
|
|
151
40
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
});
|
|
172
|
-
inserts++;
|
|
41
|
+
function redistribute() {
|
|
42
|
+
if (!config || config.excluded) return;
|
|
43
|
+
|
|
44
|
+
// Sélecteurs spécifiques à NodeBB 4.x / Harmony
|
|
45
|
+
const topicItems = document.querySelectorAll('[component="category/topic"]');
|
|
46
|
+
const postItems = document.querySelectorAll('[component="post"]');
|
|
47
|
+
|
|
48
|
+
let items = [], kind = '', interval = 10, showFirst = false;
|
|
49
|
+
|
|
50
|
+
if (topicItems.length > 0 && config.enableBetweenAds) {
|
|
51
|
+
items = Array.from(topicItems);
|
|
52
|
+
kind = 'between';
|
|
53
|
+
interval = parseInt(config.intervalPosts, 10);
|
|
54
|
+
showFirst = config.showFirstTopicAd;
|
|
55
|
+
} else if (postItems.length > 0 && config.enableMessageAds) {
|
|
56
|
+
items = Array.from(postItems);
|
|
57
|
+
kind = 'message';
|
|
58
|
+
interval = parseInt(config.messageIntervalPosts, 10);
|
|
59
|
+
showFirst = config.showFirstMessageAd;
|
|
173
60
|
}
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
61
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
inner.id = 'ezoic-pub-ad-placeholder-' + id;
|
|
195
|
-
inner.className = 'ezoic-ad';
|
|
196
|
-
d.appendChild(inner);
|
|
197
|
-
pool.appendChild(d);
|
|
62
|
+
if (items.length === 0) return;
|
|
63
|
+
|
|
64
|
+
items.forEach((item, index) => {
|
|
65
|
+
const pos = index + 1;
|
|
66
|
+
const shouldHaveAd = (pos === 1 && showFirst) || (pos % interval === 0);
|
|
67
|
+
|
|
68
|
+
const next = item.nextElementSibling;
|
|
69
|
+
if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
|
|
70
|
+
const pool = getPool();
|
|
71
|
+
const available = pool.querySelector(`.${WRAP_CLASS}[data-kind="${kind}"]`);
|
|
72
|
+
if (available) {
|
|
73
|
+
isInternalChange = true;
|
|
74
|
+
item.parentNode.insertBefore(available, item.nextSibling);
|
|
75
|
+
callEzoic(available.getAttribute('data-placeholder-id'));
|
|
76
|
+
setTimeout(() => { isInternalChange = false; }, 100);
|
|
77
|
+
}
|
|
198
78
|
}
|
|
199
|
-
});
|
|
200
|
-
};
|
|
201
|
-
setupPool(config.placeholderIds, 'between');
|
|
202
|
-
setupPool(config.messagePlaceholderIds, 'message');
|
|
203
|
-
cb();
|
|
204
|
-
}).catch(e => console.error('[Ezoic] Config load error', e));
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
let timer = null;
|
|
208
|
-
function schedule() {
|
|
209
|
-
if (timer) clearTimeout(timer);
|
|
210
|
-
timer = setTimeout(() => {
|
|
211
|
-
// Sélecteurs pour NodeBB 4.x / Harmony
|
|
212
|
-
const lists = document.querySelectorAll('[component="category"], .topic-list, [component="topic"], [component="category/topic/list"]');
|
|
213
|
-
lists.forEach(redistribute);
|
|
214
|
-
}, 200);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function init() {
|
|
218
|
-
fetchConfig(() => {
|
|
219
|
-
schedule();
|
|
220
|
-
if (typeof MutationObserver !== 'undefined') {
|
|
221
|
-
const mo = new MutationObserver((muts) => {
|
|
222
|
-
if (isInternalChange) return;
|
|
223
|
-
// On vérifie si un élément structurel a été ajouté
|
|
224
|
-
const shouldRun = muts.some(m => m.addedNodes.length > 0);
|
|
225
|
-
if (shouldRun) schedule();
|
|
226
79
|
});
|
|
227
|
-
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
}
|
|
80
|
+
}
|
|
231
81
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
82
|
+
function init() {
|
|
83
|
+
fetch('/api/plugins/ezoic-infinite/config')
|
|
84
|
+
.then(r => r.json())
|
|
85
|
+
.then(data => {
|
|
86
|
+
config = data;
|
|
87
|
+
const pool = getPool();
|
|
88
|
+
const setup = (raw, kind) => {
|
|
89
|
+
if (!raw) return;
|
|
90
|
+
raw.split(/[\s,]+/).filter(Boolean).forEach(id => {
|
|
91
|
+
if (!document.querySelector(`[data-placeholder-id="${id}"]`)) {
|
|
92
|
+
const d = document.createElement('div');
|
|
93
|
+
d.className = WRAP_CLASS;
|
|
94
|
+
d.setAttribute('data-kind', kind);
|
|
95
|
+
d.setAttribute('data-placeholder-id', id);
|
|
96
|
+
d.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}" class="ezoic-ad"></div>`;
|
|
97
|
+
pool.appendChild(d);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
setup(config.placeholderIds, 'between');
|
|
102
|
+
setup(config.messagePlaceholderIds, 'message');
|
|
103
|
+
|
|
104
|
+
redistribute();
|
|
105
|
+
|
|
106
|
+
// On surveille les changements de page et l'infinite scroll
|
|
107
|
+
const observer = new MutationObserver(() => {
|
|
108
|
+
if (!isInternalChange) redistribute();
|
|
109
|
+
});
|
|
110
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (document.readyState === 'loading') {
|
|
115
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
116
|
+
} else {
|
|
117
|
+
init();
|
|
118
|
+
}
|
|
237
119
|
})();
|
package/public/style.css
CHANGED
|
@@ -1,35 +1,20 @@
|
|
|
1
|
-
/*
|
|
1
|
+
/* Style du container injecté */
|
|
2
2
|
.nodebb-ezoic-wrap {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
min-height: 50px; /* Important pour que l'ad-tester détecte le bloc */
|
|
3
|
+
display: block !important;
|
|
4
|
+
width: 100% !important;
|
|
5
|
+
margin: 30px 0 !important;
|
|
6
|
+
min-height: 100px; /* Force un espace pour qu'Ezoic puisse injecter */
|
|
7
|
+
clear: both;
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
/*
|
|
12
|
-
.nodebb-ezoic-wrap.ez-orphan-hidden {
|
|
13
|
-
display: none !important;
|
|
14
|
-
height: 0 !important;
|
|
15
|
-
min-height: 0 !important;
|
|
16
|
-
margin: 0 !important;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/* Nettoyage des styles internes Ezoic */
|
|
20
|
-
.nodebb-ezoic-wrap .ezoic-ad {
|
|
21
|
-
margin: 0 auto !important;
|
|
22
|
-
padding: 0 !important;
|
|
23
|
-
min-height: 1px !important;
|
|
24
|
-
height: auto !important;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/* Neutralise les marges doubles */
|
|
10
|
+
/* ANTI-PILLUP : Interdit deux pubs consécutives */
|
|
28
11
|
.nodebb-ezoic-wrap + .nodebb-ezoic-wrap {
|
|
29
|
-
|
|
12
|
+
display: none !important;
|
|
30
13
|
}
|
|
31
14
|
|
|
32
|
-
/*
|
|
33
|
-
.ezoic-
|
|
34
|
-
|
|
15
|
+
/* Alignement Harmony */
|
|
16
|
+
[component="category/topic"] + .nodebb-ezoic-wrap,
|
|
17
|
+
[component="post"] + .nodebb-ezoic-wrap {
|
|
18
|
+
border-top: 1px solid rgba(0,0,0,0.05);
|
|
19
|
+
padding: 15px 0;
|
|
35
20
|
}
|
package/public/test.txt
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
hi
|