nodebb-plugin-ezoic-infinite 1.6.82 → 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 CHANGED
@@ -2,65 +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
- const settings = await meta.settings.get(SETTINGS_KEY);
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
- const renderAdmin = async (req, res) => {
24
- const settings = await getSettings();
25
- const names = await db.getSortedSetRange('groups:createtime', 0, -1);
26
- const groupsData = await groups.getGroupsData(names);
27
- const allGroups = groupsData.filter(g => g && g.name).map(g => ({ name: g.name }));
28
-
29
- res.render('admin/plugins/ezoic-infinite', {
30
- ...settings,
31
- allGroups,
32
- enableBetweenAds_checked: settings.enableBetweenAds === 'on' ? 'checked' : '',
33
- enableMessageAds_checked: settings.enableMessageAds === 'on' ? 'checked' : ''
34
- });
35
- };
36
-
37
- router.get('/admin/plugins/ezoic-infinite', middleware.admin.buildHeader, renderAdmin);
38
- router.get('/api/admin/plugins/ezoic-infinite', renderAdmin);
39
-
40
- router.get('/api/plugins/ezoic-infinite/config', async (req, res) => {
41
- const settings = await getSettings();
42
- const excluded = await isUserExcluded(req.uid, settings.excludedGroups);
43
- res.json({
44
- excluded,
45
- enableBetweenAds: settings.enableBetweenAds === 'on',
46
- showFirstTopicAd: settings.showFirstTopicAd === 'on',
47
- placeholderIds: settings.placeholderIds || '',
48
- intervalPosts: settings.intervalPosts || 10,
49
- enableMessageAds: settings.enableMessageAds === 'on',
50
- showFirstMessageAd: settings.showFirstMessageAd === 'on',
51
- messagePlaceholderIds: settings.messagePlaceholderIds || '',
52
- messageIntervalPosts: settings.messageIntervalPosts || 10,
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
+ });
53
51
  });
54
- });
55
52
  };
56
53
 
57
54
  plugin.addAdminNavigation = async (header) => {
58
- header.plugins.push({
59
- route: '/plugins/ezoic-infinite',
60
- icon: 'fa-ad',
61
- name: 'Ezoic Infinite'
62
- });
63
- return header;
55
+ header.plugins.push({ route: '/plugins/ezoic-infinite', icon: 'fa-ad', name: 'Ezoic Infinite' });
56
+ return header;
64
57
  };
65
58
 
66
59
  module.exports = plugin;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.6.82",
3
+ "version": "1.6.83",
4
4
  "description": "Production-ready Ezoic infinite ads integration for NodeBB 4.x",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
package/public/client.js CHANGED
@@ -1,7 +1,6 @@
1
1
  (function () {
2
2
  'use strict';
3
3
 
4
- // Singleton pour éviter les conflits lors de la navigation AJAX de NodeBB
5
4
  if (window.ezInfiniteInjected) return;
6
5
  window.ezInfiniteInjected = true;
7
6
 
@@ -10,21 +9,7 @@
10
9
 
11
10
  let config = null;
12
11
  let isInternalChange = false;
13
- let ezEnabledInternally = false;
14
-
15
- // Détection du sens du scroll pour le nettoyage (Pillup prevention)
16
- let lastScrollY = window.scrollY;
17
- let scrollDir = 1;
18
- window.addEventListener('scroll', () => {
19
- const y = window.scrollY;
20
- scrollDir = y > lastScrollY ? 1 : -1;
21
- lastScrollY = y;
22
- }, { passive: true });
23
-
24
- function withInternalDomChange(fn) {
25
- isInternalChange = true;
26
- try { fn(); } finally { isInternalChange = false; }
27
- }
12
+ let ezEnabled = false;
28
13
 
29
14
  function getPool() {
30
15
  let p = document.getElementById(POOL_ID);
@@ -37,115 +22,40 @@
37
22
  return p;
38
23
  }
39
24
 
40
- function releaseWrapNode(wrap) {
41
- if (!wrap || !wrap.parentNode) return;
42
- const pool = getPool();
43
- wrap.classList.remove('ez-orphan-hidden');
44
- if (wrap.parentNode !== pool) {
45
- pool.appendChild(wrap);
46
- }
47
- }
48
-
49
- // ANTI-PILLUP : Supprime les pubs si elles se touchent
50
- function decluster(container) {
51
- const wraps = Array.from(container.querySelectorAll(`.${WRAP_CLASS}`));
52
- wraps.forEach(wrap => {
53
- let next = wrap.nextElementSibling;
54
- // Si l'élément suivant est une autre pub, on la remet au pool
55
- if (next && next.classList.contains(WRAP_CLASS)) {
56
- withInternalDomChange(() => releaseWrapNode(next));
57
- }
58
- });
59
- }
60
-
61
- // Nettoyage des orphelins (Virtualisation NodeBB)
62
- function pruneOrphanWraps() {
63
- const wraps = document.querySelectorAll(`.${WRAP_CLASS}`);
64
- const items = document.querySelectorAll('[component="category/topic"], [component="post"]');
65
- const itemSet = new Set(items);
66
-
67
- wraps.forEach(wrap => {
68
- if (wrap.parentElement && wrap.parentElement.id === POOL_ID) return;
69
-
70
- let hasNeighbor = false;
71
- let p = wrap.previousElementSibling;
72
- // On vérifie sur 2 voisins pour être sûr
73
- for (let i = 0; i < 2 && p; i++) {
74
- if (itemSet.has(p)) { hasNeighbor = true; break; }
75
- p = p.previousElementSibling;
76
- }
77
- if (!hasNeighbor) {
78
- let n = wrap.nextElementSibling;
79
- for (let i = 0; i < 2 && n; i++) {
80
- if (itemSet.has(n)) { hasNeighbor = true; break; }
81
- n = n.nextElementSibling;
82
- }
83
- }
84
-
85
- if (!hasNeighbor) {
86
- // En remontée (Up), on recycle immédiatement pour éviter l'empilement
87
- if (scrollDir === -1) {
88
- withInternalDomChange(() => releaseWrapNode(wrap));
89
- } else {
90
- wrap.classList.add('ez-orphan-hidden');
91
- }
92
- } else {
93
- wrap.classList.remove('ez-orphan-hidden');
94
- }
95
- });
96
- }
97
-
98
- // Appel à la bibliothèque Ezoic déjà initialisée
99
- function callEzoicRefresh(placeholderId) {
25
+ function callEzoic(pid) {
100
26
  if (typeof window.ezstandalone === 'undefined') return;
101
- const pid = parseInt(placeholderId, 10);
102
-
27
+ const id = parseInt(pid, 10);
103
28
  try {
104
- // On définit l'ID
105
- window.ezstandalone.define(pid);
106
-
107
- // Si c'est le tout premier, on tente un display
108
- if (!ezEnabledInternally) {
29
+ window.ezstandalone.define(id);
30
+ if (!ezEnabled) {
109
31
  window.ezstandalone.enable();
110
32
  window.ezstandalone.display();
111
- ezEnabledInternally = true;
33
+ ezEnabled = true;
112
34
  } else {
113
- // Sinon on rafraîchit (avec un petit délai pour le rendu DOM)
114
- setTimeout(() => {
115
- const el = document.getElementById('ezoic-pub-ad-placeholder-' + pid);
116
- if (el && el.offsetParent !== null) {
117
- window.ezstandalone.refresh();
118
- }
119
- }, 250);
35
+ // Délai pour s'assurer que le div est bien rendu par le navigateur
36
+ setTimeout(() => { window.ezstandalone.refresh(); }, 200);
120
37
  }
121
- } catch (e) {
122
- console.warn('[Ezoic-Infinite] Refresh skip:', e.message);
123
- }
38
+ } catch (e) { console.warn('[Ezoic] Error:', e); }
124
39
  }
125
40
 
126
- function redistribute(container) {
127
- if (!container || !config || config.excluded) return;
128
-
129
- pruneOrphanWraps();
130
- decluster(container);
41
+ function redistribute() {
42
+ if (!config || config.excluded) return;
131
43
 
132
- const isTopicList = container.getAttribute('component') === 'category' || container.classList.contains('topic-list');
133
- const isPostList = container.getAttribute('component') === 'topic' || container.querySelectorAll('[component="post"]').length > 0;
44
+ // Sélecteurs spécifiques à NodeBB 4.x / Harmony
45
+ const topicItems = document.querySelectorAll('[component="category/topic"]');
46
+ const postItems = document.querySelectorAll('[component="post"]');
134
47
 
135
- let interval = 10;
136
- let kind = '';
137
- let items = [];
138
- let showFirst = false;
48
+ let items = [], kind = '', interval = 10, showFirst = false;
139
49
 
140
- if (isTopicList && config.enableBetweenAds) {
141
- interval = parseInt(config.intervalPosts, 10) || 10;
50
+ if (topicItems.length > 0 && config.enableBetweenAds) {
51
+ items = Array.from(topicItems);
142
52
  kind = 'between';
143
- items = Array.from(container.querySelectorAll('[component="category/topic"]'));
53
+ interval = parseInt(config.intervalPosts, 10);
144
54
  showFirst = config.showFirstTopicAd;
145
- } else if (isPostList && config.enableMessageAds) {
146
- interval = parseInt(config.messageIntervalPosts, 10) || 10;
55
+ } else if (postItems.length > 0 && config.enableMessageAds) {
56
+ items = Array.from(postItems);
147
57
  kind = 'message';
148
- items = Array.from(container.querySelectorAll('[component="post"]'));
58
+ interval = parseInt(config.messageIntervalPosts, 10);
149
59
  showFirst = config.showFirstMessageAd;
150
60
  }
151
61
 
@@ -153,32 +63,22 @@
153
63
 
154
64
  items.forEach((item, index) => {
155
65
  const pos = index + 1;
156
- let shouldHaveAd = (pos % interval === 0);
157
- if (pos === 1 && showFirst) shouldHaveAd = true;
158
-
66
+ const shouldHaveAd = (pos === 1 && showFirst) || (pos % interval === 0);
67
+
159
68
  const next = item.nextElementSibling;
160
69
  if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
161
70
  const pool = getPool();
162
71
  const available = pool.querySelector(`.${WRAP_CLASS}[data-kind="${kind}"]`);
163
72
  if (available) {
164
- withInternalDomChange(() => {
165
- item.parentNode.insertBefore(available, item.nextSibling);
166
- callEzoicRefresh(available.getAttribute('data-placeholder-id'));
167
- });
73
+ isInternalChange = true;
74
+ item.parentNode.insertBefore(available, item.nextSibling);
75
+ callEzoic(available.getAttribute('data-placeholder-id'));
76
+ setTimeout(() => { isInternalChange = false; }, 100);
168
77
  }
169
78
  }
170
79
  });
171
80
  }
172
81
 
173
- let timer = null;
174
- function schedule() {
175
- if (timer) clearTimeout(timer);
176
- timer = setTimeout(() => {
177
- const targets = document.querySelectorAll('[component="category"], .topic-list, [component="topic"], [component="category/topic/list"]');
178
- targets.forEach(redistribute);
179
- }, 300);
180
- }
181
-
182
82
  function init() {
183
83
  fetch('/api/plugins/ezoic-infinite/config')
184
84
  .then(r => r.json())
@@ -201,14 +101,13 @@
201
101
  setup(config.placeholderIds, 'between');
202
102
  setup(config.messagePlaceholderIds, 'message');
203
103
 
204
- schedule();
205
-
206
- if (typeof MutationObserver !== 'undefined') {
207
- const mo = new MutationObserver(() => {
208
- if (!isInternalChange) schedule();
209
- });
210
- mo.observe(document.body, { childList: true, subtree: true });
211
- }
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 });
212
111
  });
213
112
  }
214
113
 
package/public/style.css CHANGED
@@ -1,37 +1,20 @@
1
- /* Container de base */
1
+ /* Style du container injecté */
2
2
  .nodebb-ezoic-wrap {
3
- display: block;
4
- width: 100%;
5
- margin: 30px auto !important;
6
- padding: 0 !important;
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
7
  clear: both;
8
- min-height: 100px; /* Important pour Ezoic */
9
8
  }
10
9
 
11
- /* ANTI-PILLUP RADICAL : Si deux pubs se suivent, on cache la 2ème immédiatement par CSS */
10
+ /* ANTI-PILLUP : Interdit deux pubs consécutives */
12
11
  .nodebb-ezoic-wrap + .nodebb-ezoic-wrap {
13
12
  display: none !important;
14
- height: 0 !important;
15
- margin: 0 !important;
16
13
  }
17
14
 
18
- /* Cache les blocs orphelins (ceux qui ont perdu leurs posts voisins au scroll up) */
19
- .nodebb-ezoic-wrap.ez-orphan-hidden {
20
- display: none !important;
21
- height: 0 !important;
22
- margin: 0 !important;
23
- padding: 0 !important;
24
- }
25
-
26
- /* Évite les espaces blancs inutiles à l'intérieur */
27
- .nodebb-ezoic-wrap .ezoic-ad {
28
- margin: 0 auto !important;
29
- min-height: 1px !important;
30
- }
31
-
32
- /* Harmonisation NodeBB 4.x / Harmony */
33
- [component="category"] .nodebb-ezoic-wrap,
34
- .topic-list .nodebb-ezoic-wrap {
15
+ /* Alignement Harmony */
16
+ [component="category/topic"] + .nodebb-ezoic-wrap,
17
+ [component="post"] + .nodebb-ezoic-wrap {
35
18
  border-top: 1px solid rgba(0,0,0,0.05);
36
- padding-top: 10px !important;
19
+ padding: 15px 0;
37
20
  }
package/public/test.txt DELETED
@@ -1 +0,0 @@
1
- hi