nodebb-plugin-ezoic-infinite 1.6.53 → 1.6.55

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-ezoic-infinite",
3
- "version": "1.6.53",
3
+ "version": "1.6.55",
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,30 +1,16 @@
1
1
  (function () {
2
2
  'use strict';
3
3
 
4
- // --- 1. GESTION DU SCROLL ---
5
- let lastScrollY = 0;
6
- let scrollDir = 1;
7
- try {
8
- lastScrollY = window.scrollY || 0;
9
- window.addEventListener('scroll', () => {
10
- const y = window.scrollY || 0;
11
- const d = y - lastScrollY;
12
- if (Math.abs(d) > 4) {
13
- scrollDir = d > 0 ? 1 : -1;
14
- lastScrollY = y;
15
- }
16
- }, { passive: true });
17
- } catch (e) {}
18
-
4
+ // --- 1. VARIABLES ET CONFIG ---
19
5
  const WRAP_CLASS = 'nodebb-ezoic-wrap';
20
- const PLACEHOLDER_PREFIX = 'ezoic-pub-ad-placeholder-';
21
6
  const PINNED_ATTR = 'data-ezoic-pinned';
22
-
7
+ const PLACEHOLDER_PREFIX = 'ezoic-pub-ad-placeholder-';
8
+
23
9
  let config = null;
24
10
  let usedIds = new Set();
25
11
  let scheduleTimer = null;
26
12
 
27
- // --- 2. SÉLECTEURS DE THÈMES (Harmony/Persona) ---
13
+ // --- 2. SÉLECTEURS DE THÈMES ---
28
14
  function getTopicList() {
29
15
  return document.querySelector('.category [component="category"], .categories [component="categories"], [component="topic/list"], .topic-list');
30
16
  }
@@ -33,28 +19,7 @@
33
19
  return document.querySelector('[component="topic"], [component="post/list"]');
34
20
  }
35
21
 
36
- // --- 3. FIX ANTI-PILEUP / ANTI-REMONTÉE ---
37
- function pruneOrphans(ul) {
38
- if (!ul) return;
39
- const wraps = ul.querySelectorAll('.' + WRAP_CLASS);
40
- wraps.forEach(wrap => {
41
- // Si c'est la pub après le 1er sujet, on la force à rester collée au 1er LI
42
- if (wrap.getAttribute(PINNED_ATTR) === 'true') {
43
- const firstLi = ul.querySelector('li[data-tid], li:first-child');
44
- if (firstLi && wrap.previousElementSibling !== firstLi) {
45
- firstLi.after(wrap);
46
- }
47
- return;
48
- }
49
- // Si on remonte, on supprime les pubs qui perdent leur "ancre" (le LI précédent)
50
- // NodeBB détruit les LI du haut pour la performance, on doit suivre le mouvement
51
- if (scrollDir === -1 && (!wrap.previousElementSibling || wrap.previousElementSibling.tagName !== 'LI')) {
52
- wrap.remove();
53
- }
54
- });
55
- }
56
-
57
- // --- 4. LOGIQUE EZOIC (Pools & Refresh) ---
22
+ // --- 3. LOGIQUE DES POOLS D'ID ---
58
23
  function getNextId(poolStr) {
59
24
  if (!poolStr) return null;
60
25
  const ids = poolStr.split(/[\s,]+/).filter(Boolean);
@@ -68,6 +33,7 @@
68
33
  return null;
69
34
  }
70
35
 
36
+ // --- 4. APPEL EZOIC (REFRESH & SHOW) ---
71
37
  function callEzoic(id) {
72
38
  if (window.ezstandalone) {
73
39
  try {
@@ -83,27 +49,34 @@
83
49
  }
84
50
  }
85
51
 
86
- // --- 5. INJECTION ---
52
+ // --- 5. INJECTION SÉCURISÉE (ANTI-PILEUP) ---
87
53
  function inject() {
88
54
  if (!config || config.excluded) return;
89
55
 
90
- // A. Gestion des SUJETS (Liste de catégories/topics)
56
+ // A. LISTE DES SUJETS (Topics)
91
57
  const ul = getTopicList();
92
58
  if (ul && config.enableBetweenAds) {
93
- pruneOrphans(ul);
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
+
94
65
  const items = Array.from(ul.children).filter(c => c.tagName === 'LI' && !c.classList.contains(WRAP_CLASS));
95
-
96
- // Pub 1 (Pinned)
97
- if (config.showFirstTopicAd && !ul.querySelector(`[${PINNED_ATTR}="true"]`)) {
98
- insertAd(items[0], getNextId(config.placeholderIds), true);
99
- }
66
+ if (items.length > 0) {
67
+ // Pub 1 (Pinned)
68
+ if (config.showFirstTopicAd && !ul.querySelector(`[${PINNED_ATTR}="true"]`)) {
69
+ insertAd(items[0], getNextId(config.placeholderIds), true);
70
+ }
100
71
 
101
- // Intervalle (uniquement en scroll vers le bas)
102
- if (scrollDir === 1) {
72
+ // Pubs d'intervalles
103
73
  const interval = parseInt(config.intervalPosts, 10) || 5;
104
74
  items.forEach((li, idx) => {
105
- if ((idx + 1) % interval === 0 && idx > 0) {
106
- if (!li.nextElementSibling?.classList.contains(WRAP_CLASS)) {
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)) {
107
80
  insertAd(li, getNextId(config.placeholderIds), false);
108
81
  }
109
82
  }
@@ -111,14 +84,25 @@
111
84
  }
112
85
  }
113
86
 
114
- // B. Gestion des MESSAGES (Post list)
87
+ // B. LISTE DES MESSAGES (Posts)
115
88
  const postList = getPostList();
116
89
  if (postList && config.enableMessageAds) {
117
- const posts = Array.from(postList.querySelectorAll('[component="post"]')).filter(p => !p.closest('.' + WRAP_CLASS));
90
+ const posts = Array.from(postList.querySelectorAll('[component="post"]'));
118
91
  if (config.showFirstMessageAd && posts.length > 0 && !postList.querySelector('.ezoic-msg-first')) {
119
92
  insertAd(posts[0], getNextId(config.messagePlaceholderIds), false, 'ezoic-msg-first');
120
93
  }
121
- // Logique d'intervalle message ici si besoin (similaire aux topics)
94
+
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
+ });
105
+ }
122
106
  }
123
107
  }
124
108
 
@@ -133,36 +117,45 @@
133
117
  wrap.appendChild(placeholder);
134
118
 
135
119
  target.after(wrap);
136
- callEzoic(id);
120
+
121
+ // On laisse le DOM respirer 50ms avant d'appeler Ezoic
122
+ setTimeout(() => callEzoic(id), 50);
137
123
  }
138
124
 
139
- // --- 6. INITIALISATION ---
125
+ // --- 6. INITIALISATION ET HOOKS ---
140
126
  async function fetchConfig() {
141
127
  try {
142
128
  const res = await fetch('/api/plugins/ezoic-infinite/config');
143
129
  config = await res.json();
144
- } catch (e) {}
130
+ } catch (e) { console.error('[ezoic] config fetch failed'); }
145
131
  }
146
132
 
147
133
  function schedule() {
148
134
  if (scheduleTimer) clearTimeout(scheduleTimer);
149
- scheduleTimer = setTimeout(inject, 200);
135
+ scheduleTimer = setTimeout(inject, 250);
150
136
  }
151
137
 
152
138
  function init() {
153
139
  fetchConfig().then(() => {
154
140
  schedule();
155
- const ul = getTopicList();
156
- if (ul && typeof MutationObserver !== 'undefined') {
157
- new MutationObserver((m) => {
158
- if (m.some(record => record.addedNodes.length > 0)) schedule();
159
- }).observe(ul, { childList: true });
160
- }
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 });
161
153
  });
162
154
 
155
+ // Compatibilité jQuery/NodeBB Events
163
156
  if (window.jQuery) {
164
157
  window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', () => {
165
- setTimeout(schedule, 400);
158
+ setTimeout(schedule, 500);
166
159
  });
167
160
  }
168
161
  }
package/public/style.css CHANGED
@@ -86,3 +86,15 @@ li.nodebb-ezoic-host { list-style: none; width: 100%; display: block; }
86
86
  li.nodebb-ezoic-host > .nodebb-ezoic-wrap.ezoic-ad-between { width: 100%; display: block; }
87
87
  /* ===== /V17 ===== */
88
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 */
95
+ }
96
+
97
+ /* Si la pub est vide, on garde un petit espace pour éviter le saut au scroll up */
98
+ .nodebb-ezoic-wrap:empty {
99
+ min-height: 1px;
100
+ }