nodebb-plugin-ezoic-infinite 1.6.82 → 1.6.84

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.84",
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,8 @@
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;
13
+ let definedIds = new Set();
28
14
 
29
15
  function getPool() {
30
16
  let p = document.getElementById(POOL_ID);
@@ -37,115 +23,56 @@
37
23
  return p;
38
24
  }
39
25
 
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) {
26
+ function callEzoic(pid) {
100
27
  if (typeof window.ezstandalone === 'undefined') return;
101
- const pid = parseInt(placeholderId, 10);
102
-
28
+ const id = parseInt(pid, 10);
29
+ if (isNaN(id)) return;
30
+
103
31
  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) {
32
+ // 1. On définit TOUJOURS l'ID avant toute autre action
33
+ window.ezstandalone.define(id);
34
+ definedIds.add(id);
35
+
36
+ // 2. Si c'est la toute première fois de la page
37
+ if (!ezEnabled) {
109
38
  window.ezstandalone.enable();
110
39
  window.ezstandalone.display();
111
- ezEnabledInternally = true;
40
+ ezEnabled = true;
41
+ console.log('[Ezoic-Infinite] Initialized with ID:', id);
112
42
  } else {
113
- // Sinon on rafraîchit (avec un petit délai pour le rendu DOM)
43
+ // 3. Sinon, on utilise refresh avec un léger délai
44
+ // pour s'assurer que le navigateur a fini de dessiner le DIV
114
45
  setTimeout(() => {
115
- const el = document.getElementById('ezoic-pub-ad-placeholder-' + pid);
116
- if (el && el.offsetParent !== null) {
46
+ const el = document.getElementById('ezoic-pub-ad-placeholder-' + id);
47
+ if (el) {
117
48
  window.ezstandalone.refresh();
49
+ console.log('[Ezoic-Infinite] Refresh called for:', id);
118
50
  }
119
- }, 250);
51
+ }, 300);
120
52
  }
121
53
  } catch (e) {
122
- console.warn('[Ezoic-Infinite] Refresh skip:', e.message);
54
+ console.warn('[Ezoic-Infinite] JS Error:', e.message);
123
55
  }
124
56
  }
125
57
 
126
- function redistribute(container) {
127
- if (!container || !config || config.excluded) return;
128
-
129
- pruneOrphanWraps();
130
- decluster(container);
58
+ function redistribute() {
59
+ if (!config || config.excluded) return;
131
60
 
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;
61
+ // Ciblage Harmony NodeBB 4
62
+ const topicItems = document.querySelectorAll('[component="category/topic"]');
63
+ const postItems = document.querySelectorAll('[component="post"]');
134
64
 
135
- let interval = 10;
136
- let kind = '';
137
- let items = [];
138
- let showFirst = false;
65
+ let items = [], kind = '', interval = 10, showFirst = false;
139
66
 
140
- if (isTopicList && config.enableBetweenAds) {
141
- interval = parseInt(config.intervalPosts, 10) || 10;
67
+ if (topicItems.length > 0 && config.enableBetweenAds) {
68
+ items = Array.from(topicItems);
142
69
  kind = 'between';
143
- items = Array.from(container.querySelectorAll('[component="category/topic"]'));
70
+ interval = parseInt(config.intervalPosts, 10);
144
71
  showFirst = config.showFirstTopicAd;
145
- } else if (isPostList && config.enableMessageAds) {
146
- interval = parseInt(config.messageIntervalPosts, 10) || 10;
72
+ } else if (postItems.length > 0 && config.enableMessageAds) {
73
+ items = Array.from(postItems);
147
74
  kind = 'message';
148
- items = Array.from(container.querySelectorAll('[component="post"]'));
75
+ interval = parseInt(config.messageIntervalPosts, 10);
149
76
  showFirst = config.showFirstMessageAd;
150
77
  }
151
78
 
@@ -153,32 +80,24 @@
153
80
 
154
81
  items.forEach((item, index) => {
155
82
  const pos = index + 1;
156
- let shouldHaveAd = (pos % interval === 0);
157
- if (pos === 1 && showFirst) shouldHaveAd = true;
158
-
83
+ const shouldHaveAd = (pos === 1 && showFirst) || (pos % interval === 0);
84
+
159
85
  const next = item.nextElementSibling;
160
86
  if (shouldHaveAd && !(next && next.classList.contains(WRAP_CLASS))) {
161
87
  const pool = getPool();
162
88
  const available = pool.querySelector(`.${WRAP_CLASS}[data-kind="${kind}"]`);
163
89
  if (available) {
164
- withInternalDomChange(() => {
165
- item.parentNode.insertBefore(available, item.nextSibling);
166
- callEzoicRefresh(available.getAttribute('data-placeholder-id'));
167
- });
90
+ isInternalChange = true;
91
+ // Insertion physique dans le DOM
92
+ item.parentNode.insertBefore(available, item.nextSibling);
93
+ // Activation Ezoic
94
+ callEzoic(available.getAttribute('data-placeholder-id'));
95
+ setTimeout(() => { isInternalChange = false; }, 100);
168
96
  }
169
97
  }
170
98
  });
171
99
  }
172
100
 
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
101
  function init() {
183
102
  fetch('/api/plugins/ezoic-infinite/config')
184
103
  .then(r => r.json())
@@ -201,14 +120,12 @@
201
120
  setup(config.placeholderIds, 'between');
202
121
  setup(config.messagePlaceholderIds, 'message');
203
122
 
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
- }
123
+ redistribute();
124
+
125
+ const observer = new MutationObserver(() => {
126
+ if (!isInternalChange) redistribute();
127
+ });
128
+ observer.observe(document.body, { childList: true, subtree: true });
212
129
  });
213
130
  }
214
131
 
package/public/style.css CHANGED
@@ -1,37 +1,18 @@
1
- /* Container de base */
2
1
  .nodebb-ezoic-wrap {
3
- display: block;
4
- width: 100%;
5
- margin: 30px auto !important;
6
- padding: 0 !important;
2
+ display: block !important;
3
+ width: 100% !important;
4
+ margin: 30px 0 !important;
5
+ min-height: 250px; /* Taille minimum pour forcer le chargement */
7
6
  clear: both;
8
- min-height: 100px; /* Important pour Ezoic */
7
+ overflow: visible !important;
9
8
  }
10
9
 
11
- /* ANTI-PILLUP RADICAL : Si deux pubs se suivent, on cache la 2ème immédiatement par CSS */
12
- .nodebb-ezoic-wrap + .nodebb-ezoic-wrap {
13
- display: none !important;
14
- height: 0 !important;
15
- margin: 0 !important;
16
- }
17
-
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
10
  .nodebb-ezoic-wrap .ezoic-ad {
28
- margin: 0 auto !important;
29
- min-height: 1px !important;
11
+ min-width: 1px;
12
+ min-height: 1px;
30
13
  }
31
14
 
32
- /* Harmonisation NodeBB 4.x / Harmony */
33
- [component="category"] .nodebb-ezoic-wrap,
34
- .topic-list .nodebb-ezoic-wrap {
35
- border-top: 1px solid rgba(0,0,0,0.05);
36
- padding-top: 10px !important;
15
+ /* Évite que les pubs se collent */
16
+ .nodebb-ezoic-wrap + .nodebb-ezoic-wrap {
17
+ display: none !important;
37
18
  }
package/public/test.txt DELETED
@@ -1 +0,0 @@
1
- hi