nodebb-plugin-ezoic-infinite 1.6.87 → 1.6.89
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 +46 -40
- package/package.json +1 -1
- package/public/client.js +57 -41
- package/public/style.css +21 -12
package/library.js
CHANGED
|
@@ -2,58 +2,64 @@
|
|
|
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');
|
|
5
6
|
|
|
6
7
|
const SETTINGS_KEY = 'ezoic-infinite';
|
|
7
8
|
const plugin = {};
|
|
8
9
|
|
|
9
10
|
async function getSettings() {
|
|
10
|
-
|
|
11
|
+
return await meta.settings.get(SETTINGS_KEY);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function isUserExcluded(uid, excludedGroups) {
|
|
15
|
+
if (!uid || !excludedGroups || !excludedGroups.length) return false;
|
|
16
|
+
const userGroups = await groups.getUserGroupsNames([uid]);
|
|
17
|
+
return excludedGroups.some(g => userGroups[0].includes(g));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function getAllGroups() {
|
|
21
|
+
let names = await db.getSortedSetRange('groups:createtime', 0, -1);
|
|
22
|
+
const data = await groups.getGroupsData(names);
|
|
23
|
+
return data.filter(g => g && g.name).map(g => ({ name: g.name }));
|
|
11
24
|
}
|
|
12
25
|
|
|
13
26
|
plugin.init = async ({ router, middleware }) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
});
|
|
27
|
+
async function render(req, res) {
|
|
28
|
+
const settings = await getSettings();
|
|
29
|
+
const allGroups = await getAllGroups();
|
|
30
|
+
res.render('admin/plugins/ezoic-infinite', {
|
|
31
|
+
...settings,
|
|
32
|
+
allGroups,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, render);
|
|
37
|
+
router.get('/api/admin/plugins/ezoic-infinite', render);
|
|
38
|
+
|
|
39
|
+
router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
|
|
40
|
+
const settings = await getSettings();
|
|
41
|
+
const excluded = await isUserExcluded(req.uid, settings.excludedGroups || []);
|
|
42
|
+
res.json({
|
|
43
|
+
excluded,
|
|
44
|
+
enableBetweenAds: settings.enableBetweenAds === 'on',
|
|
45
|
+
showFirstTopicAd: settings.showFirstTopicAd === 'on',
|
|
46
|
+
placeholderIds: settings.placeholderIds,
|
|
47
|
+
intervalPosts: settings.intervalPosts || 10,
|
|
48
|
+
enableMessageAds: settings.enableMessageAds === 'on',
|
|
49
|
+
showFirstMessageAd: settings.showFirstMessageAd === 'on',
|
|
50
|
+
messagePlaceholderIds: settings.messagePlaceholderIds,
|
|
51
|
+
messageIntervalPosts: settings.messageIntervalPosts || 10,
|
|
51
52
|
});
|
|
53
|
+
});
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
plugin.addAdminNavigation = async (header) => {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
header.plugins.push({
|
|
58
|
+
route: '/plugins/ezoic-infinite',
|
|
59
|
+
icon: 'fa-ad',
|
|
60
|
+
name: 'Ezoic Infinite'
|
|
61
|
+
});
|
|
62
|
+
return header;
|
|
57
63
|
};
|
|
58
64
|
|
|
59
65
|
module.exports = plugin;
|
package/package.json
CHANGED
package/public/client.js
CHANGED
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
|
|
10
10
|
let config = null;
|
|
11
11
|
let isInternalChange = false;
|
|
12
|
-
let
|
|
12
|
+
let definedIds = new Set();
|
|
13
|
+
let pendingIds = new Set();
|
|
14
|
+
let refreshTimer = null;
|
|
13
15
|
|
|
14
16
|
function getPool() {
|
|
15
17
|
let p = document.getElementById(POOL_ID);
|
|
@@ -22,48 +24,64 @@
|
|
|
22
24
|
return p;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
//
|
|
26
|
-
function
|
|
27
|
-
if (typeof window.ezstandalone === 'undefined') return;
|
|
28
|
-
|
|
27
|
+
// Cette fonction centralise les appels pour éviter de spammer Refresh()
|
|
28
|
+
function triggerEzoic() {
|
|
29
|
+
if (typeof window.ezstandalone === 'undefined' || pendingIds.size === 0) return;
|
|
30
|
+
|
|
29
31
|
window.ezstandalone.cmd.push(function() {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
window.ezstandalone.refresh();
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
32
|
+
const idsToProcess = Array.from(pendingIds);
|
|
33
|
+
pendingIds.clear();
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
// 1. Définir les nouveaux IDs
|
|
36
|
+
window.ezstandalone.define(idsToProcess);
|
|
38
37
|
|
|
38
|
+
// 2. Si c'est le tout premier appel de la page
|
|
39
39
|
if (!window.ezstandalone.enabled) {
|
|
40
40
|
window.ezstandalone.enable();
|
|
41
41
|
window.ezstandalone.display();
|
|
42
42
|
} else {
|
|
43
|
-
|
|
43
|
+
// 3. Sinon, on utilise Refresh MAIS seulement si on n'est pas
|
|
44
|
+
// dans la même micro-seconde que l'initialisation
|
|
45
|
+
try {
|
|
46
|
+
window.ezstandalone.refresh();
|
|
47
|
+
} catch (e) {
|
|
48
|
+
console.warn('[Ezoic] Refresh skipped to prevent conflict');
|
|
49
|
+
}
|
|
44
50
|
}
|
|
45
51
|
});
|
|
46
52
|
}
|
|
47
53
|
|
|
54
|
+
function callEzoic(id) {
|
|
55
|
+
const pid = parseInt(id, 10);
|
|
56
|
+
if (isNaN(pid)) return;
|
|
57
|
+
|
|
58
|
+
// On ajoute l'ID à la liste des IDs à traiter
|
|
59
|
+
pendingIds.add(pid);
|
|
60
|
+
definedIds.add(pid);
|
|
61
|
+
|
|
62
|
+
// On attend 200ms que les autres pubs du même scroll arrivent
|
|
63
|
+
// avant de lancer l'appel Ezoic unique. C'est le "Debounce".
|
|
64
|
+
clearTimeout(refreshTimer);
|
|
65
|
+
refreshTimer = setTimeout(triggerEzoic, 200);
|
|
66
|
+
}
|
|
67
|
+
|
|
48
68
|
function redistribute() {
|
|
49
69
|
if (!config || config.excluded) return;
|
|
50
70
|
|
|
51
|
-
//
|
|
71
|
+
// Sélecteurs Harmony
|
|
52
72
|
const topicItems = document.querySelectorAll('li[component="category/topic"]');
|
|
53
|
-
const postItems = document.querySelectorAll('[component="post"]
|
|
73
|
+
const postItems = document.querySelectorAll('[component="post"]');
|
|
54
74
|
|
|
55
|
-
// Injection pour la page d'accueil / catégories
|
|
56
75
|
if (topicItems.length > 0 && config.enableBetweenAds) {
|
|
57
|
-
|
|
76
|
+
processItems(Array.from(topicItems), 'between', config.intervalPosts, config.showFirstTopicAd);
|
|
58
77
|
}
|
|
59
|
-
|
|
60
|
-
// Injection pour les messages (topics)
|
|
78
|
+
|
|
61
79
|
if (postItems.length > 0 && config.enableMessageAds) {
|
|
62
|
-
|
|
80
|
+
processItems(Array.from(postItems), 'message', config.messageIntervalPosts, config.showFirstMessageAd);
|
|
63
81
|
}
|
|
64
82
|
}
|
|
65
83
|
|
|
66
|
-
function
|
|
84
|
+
function processItems(items, kind, interval, showFirst) {
|
|
67
85
|
const int = parseInt(interval, 10) || 10;
|
|
68
86
|
items.forEach((item, index) => {
|
|
69
87
|
const pos = index + 1;
|
|
@@ -73,15 +91,10 @@
|
|
|
73
91
|
if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
|
|
74
92
|
const pool = getPool();
|
|
75
93
|
const available = pool.querySelector(`.${WRAP_CLASS}[data-kind="${kind}"]`);
|
|
76
|
-
|
|
77
94
|
if (available) {
|
|
78
95
|
isInternalChange = true;
|
|
79
|
-
// On insère l'élément comme dans le zip original
|
|
80
96
|
item.parentNode.insertBefore(available, item.nextSibling);
|
|
81
|
-
|
|
82
|
-
const pid = parseInt(available.getAttribute('data-placeholder-id'), 10);
|
|
83
|
-
callEzoic(pid);
|
|
84
|
-
|
|
97
|
+
callEzoic(available.getAttribute('data-placeholder-id'));
|
|
85
98
|
setTimeout(() => { isInternalChange = false; }, 100);
|
|
86
99
|
}
|
|
87
100
|
}
|
|
@@ -94,8 +107,7 @@
|
|
|
94
107
|
.then(data => {
|
|
95
108
|
config = data;
|
|
96
109
|
const pool = getPool();
|
|
97
|
-
|
|
98
|
-
const setupPool = (raw, kind) => {
|
|
110
|
+
const setup = (raw, kind) => {
|
|
99
111
|
if (!raw) return;
|
|
100
112
|
raw.split(/[\s,]+/).filter(Boolean).forEach(id => {
|
|
101
113
|
if (!document.querySelector(`[data-placeholder-id="${id}"]`)) {
|
|
@@ -103,19 +115,16 @@
|
|
|
103
115
|
d.className = WRAP_CLASS;
|
|
104
116
|
d.setAttribute('data-kind', kind);
|
|
105
117
|
d.setAttribute('data-placeholder-id', id);
|
|
106
|
-
|
|
107
|
-
d.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}" class="ezoic-ad"></div>`;
|
|
118
|
+
d.innerHTML = `<div id="ezoic-pub-ad-placeholder-${id}"></div>`;
|
|
108
119
|
pool.appendChild(d);
|
|
109
120
|
}
|
|
110
121
|
});
|
|
111
122
|
};
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
setupPool(config.messagePlaceholderIds, 'message');
|
|
123
|
+
setup(config.placeholderIds, 'between');
|
|
124
|
+
setup(config.messagePlaceholderIds, 'message');
|
|
115
125
|
|
|
116
126
|
redistribute();
|
|
117
127
|
|
|
118
|
-
// Observer pour l'infinite scroll (reprise du zip)
|
|
119
128
|
const observer = new MutationObserver(() => {
|
|
120
129
|
if (!isInternalChange) redistribute();
|
|
121
130
|
});
|
|
@@ -123,12 +132,19 @@
|
|
|
123
132
|
});
|
|
124
133
|
}
|
|
125
134
|
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
135
|
+
// Crucial pour les SPA : Nettoyer l'état d'Ezoic lors d'un changement de page
|
|
136
|
+
window.addEventListener('action:ajaxify.start', function() {
|
|
137
|
+
if (typeof window.ezstandalone !== 'undefined' && window.ezstandalone.enabled) {
|
|
138
|
+
// Optionnel : window.ezstandalone.destroy();
|
|
139
|
+
// Mais souvent il vaut mieux juste laisser Ezoic gérer
|
|
140
|
+
}
|
|
141
|
+
definedIds.clear();
|
|
142
|
+
pendingIds.clear();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
window.addEventListener('action:ajaxify.end', function() {
|
|
146
|
+
setTimeout(redistribute, 600);
|
|
147
|
+
});
|
|
132
148
|
|
|
133
149
|
if (document.readyState === 'loading') {
|
|
134
150
|
document.addEventListener('DOMContentLoaded', init);
|
package/public/style.css
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
|
+
/* Container global */
|
|
1
2
|
.nodebb-ezoic-wrap {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
display: block !important;
|
|
4
|
+
width: 100% !important;
|
|
5
|
+
min-width: 100% !important;
|
|
6
|
+
margin: 20px 0 !important;
|
|
7
|
+
min-height: 250px;
|
|
8
|
+
clear: both;
|
|
9
|
+
text-align: center;
|
|
7
10
|
}
|
|
8
11
|
|
|
9
|
-
/* Fix pour
|
|
12
|
+
/* Fix spécifique pour la liste des topics (Accueil Harmony) */
|
|
10
13
|
li[component="category/topic"] + .nodebb-ezoic-wrap {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
list-style: none !important;
|
|
15
|
+
margin-left: 0 !important;
|
|
16
|
+
padding: 15px 0;
|
|
17
|
+
border-bottom: 1px solid rgba(0,0,0,0.05);
|
|
15
18
|
}
|
|
16
19
|
|
|
17
|
-
/*
|
|
20
|
+
/* Si le bloc est vide ou n'a pas encore chargé */
|
|
18
21
|
.nodebb-ezoic-wrap:empty {
|
|
19
|
-
|
|
22
|
+
min-height: 1px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/* Neutralisation des marges internes d'Ezoic */
|
|
26
|
+
.ezoic-ad {
|
|
27
|
+
margin: 0 auto !important;
|
|
28
|
+
display: inline-block !important;
|
|
20
29
|
}
|