nodebb-plugin-ezoic-infinite 1.6.56 → 1.6.58

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.56",
3
+ "version": "1.6.58",
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
- // --- 1. VARIABLES ET CONFIG ---
5
4
  const WRAP_CLASS = 'nodebb-ezoic-wrap';
6
5
  const PINNED_ATTR = 'data-ezoic-pinned';
7
6
  const PLACEHOLDER_PREFIX = 'ezoic-pub-ad-placeholder-';
@@ -9,17 +8,43 @@
9
8
  let config = null;
10
9
  let usedIds = new Set();
11
10
  let scheduleTimer = null;
11
+ let isEzoicEnabled = false;
12
12
 
13
- // --- 2. SÉLECTEURS DE THÈMES ---
13
+ // --- SÉLECTEURS COMPATIBLES HARMONY ---
14
14
  function getTopicList() {
15
- return document.querySelector('.category [component="category"], .categories [component="categories"], [component="topic/list"], .topic-list');
15
+ return document.querySelector('[component="topic/list"], [component="category"], [component="categories"]');
16
16
  }
17
17
 
18
18
  function getPostList() {
19
19
  return document.querySelector('[component="topic"], [component="post/list"]');
20
20
  }
21
21
 
22
- // --- 3. LOGIQUE DES POOLS D'ID ---
22
+ // --- GESTION DU SDK EZOIC (SÉCURISÉE) ---
23
+ function callEzoic(idsToDefine) {
24
+ if (!window.ezstandalone || !idsToDefine.length) return;
25
+
26
+ try {
27
+ // 1. On définit les nouveaux placeholders
28
+ window.ezstandalone.define(idsToDefine);
29
+
30
+ // 2. On gère le cycle de vie (Correction de tes erreurs de log)
31
+ if (!isEzoicEnabled && !window.ezstandalone.enabled) {
32
+ window.ezstandalone.enable();
33
+ window.ezstandalone.display();
34
+ isEzoicEnabled = true;
35
+ } else {
36
+ // Si déjà activé, on attend un cycle CPU pour refresh proprement
37
+ setTimeout(() => {
38
+ if (window.ezstandalone.enabled) {
39
+ window.ezstandalone.refresh();
40
+ }
41
+ }, 200);
42
+ }
43
+ } catch (e) {
44
+ console.warn('[Ezoic] SDK Error:', e);
45
+ }
46
+ }
47
+
23
48
  function getNextId(poolStr) {
24
49
  if (!poolStr) return null;
25
50
  const ids = poolStr.split(/[\s,]+/).filter(Boolean);
@@ -33,131 +58,95 @@
33
58
  return null;
34
59
  }
35
60
 
36
- // --- 4. APPEL EZOIC (REFRESH & SHOW) ---
37
- function callEzoic(id) {
38
- if (window.ezstandalone) {
39
- try {
40
- const nid = parseInt(id, 10);
41
- window.ezstandalone.define(nid);
42
- if (!window.ezstandalone.enabled) {
43
- window.ezstandalone.enable();
44
- window.ezstandalone.display();
45
- } else {
46
- window.ezstandalone.refresh();
47
- }
48
- } catch (e) { console.warn('[ezoic] refresh error', id, e); }
49
- }
50
- }
51
-
52
- // --- 5. INJECTION SÉCURISÉE (ANTI-PILEUP) ---
61
+ // --- LOGIQUE D'INJECTION ---
53
62
  function inject() {
54
63
  if (!config || config.excluded) return;
55
-
56
- // A. LISTE DES SUJETS (Topics)
64
+ const newIds = [];
57
65
  const ul = getTopicList();
66
+
67
+ // A. LISTE DES SUJETS
58
68
  if (ul && config.enableBetweenAds) {
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 => {
69
+ // Nettoyage des blocs vides qui pourraient traîner
70
+ ul.querySelectorAll('.' + WRAP_CLASS).forEach(w => {
62
71
  if (!w.previousElementSibling || w.previousElementSibling.tagName !== 'LI') w.remove();
63
72
  });
64
73
 
65
74
  const items = Array.from(ul.children).filter(c => c.tagName === 'LI' && !c.classList.contains(WRAP_CLASS));
66
- if (items.length > 0) {
67
- // Pub n°1 (Pinned)
68
- if (config.showFirstTopicAd && !ul.querySelector(`[${PINNED_ATTR}="true"]`)) {
69
- insertAd(items[0], getNextId(config.placeholderIds), true);
75
+
76
+ // Pub n°1 (Après le premier sujet)
77
+ if (config.showFirstTopicAd && !ul.querySelector(`[${PINNED_ATTR}="true"]`)) {
78
+ const id = getNextId(config.placeholderIds);
79
+ if (id) {
80
+ insertAd(items[0], id, true);
81
+ newIds.push(parseInt(id, 10));
70
82
  }
83
+ }
71
84
 
72
- // Pubs d'intervalles
73
- const interval = parseInt(config.intervalPosts, 10) || 5;
74
- items.forEach((li, idx) => {
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)) {
80
- insertAd(li, getNextId(config.placeholderIds), false);
85
+ // Intervalles
86
+ const interval = parseInt(config.intervalPosts, 10) || 5;
87
+ items.forEach((li, idx) => {
88
+ if ((idx + 1) % interval === 0 && idx > 0) {
89
+ if (!li.nextElementSibling || !li.nextElementSibling.classList.contains(WRAP_CLASS)) {
90
+ const id = getNextId(config.placeholderIds);
91
+ if (id) {
92
+ insertAd(li, id, false);
93
+ newIds.push(parseInt(id, 10));
81
94
  }
82
95
  }
83
- });
84
- }
96
+ }
97
+ });
85
98
  }
86
99
 
87
- // B. LISTE DES MESSAGES (Posts)
100
+ // B. LISTE DES MESSAGES (Sujets ouverts)
88
101
  const postList = getPostList();
89
102
  if (postList && config.enableMessageAds) {
90
103
  const posts = Array.from(postList.querySelectorAll('[component="post"]'));
91
104
  if (config.showFirstMessageAd && posts.length > 0 && !postList.querySelector('.ezoic-msg-first')) {
92
- insertAd(posts[0], getNextId(config.messagePlaceholderIds), false, 'ezoic-msg-first');
93
- }
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
+ const id = getNextId(config.messagePlaceholderIds);
106
+ if (id) {
107
+ insertAd(posts[0], id, false, 'ezoic-msg-first');
108
+ newIds.push(parseInt(id, 10));
109
+ }
105
110
  }
106
111
  }
112
+
113
+ if (newIds.length > 0) {
114
+ callEzoic(newIds);
115
+ }
107
116
  }
108
117
 
109
118
  function insertAd(target, id, isPinned, extraClass = '') {
110
- if (!target || !id) return;
119
+ if (!target) return;
111
120
  const wrap = document.createElement('div');
112
121
  wrap.className = `${WRAP_CLASS} ezoic-ad-between ${extraClass}`;
113
122
  if (isPinned) wrap.setAttribute(PINNED_ATTR, 'true');
114
-
115
- const placeholder = document.createElement('div');
116
- placeholder.id = PLACEHOLDER_PREFIX + id;
117
- wrap.appendChild(placeholder);
118
-
123
+ wrap.innerHTML = `<div id="${PLACEHOLDER_PREFIX}${id}"></div>`;
119
124
  target.after(wrap);
120
-
121
- // On laisse le DOM respirer 50ms avant d'appeler Ezoic
122
- setTimeout(() => callEzoic(id), 50);
123
125
  }
124
126
 
125
- // --- 6. INITIALISATION ET HOOKS ---
126
- async function fetchConfig() {
127
+ // --- INITIALISATION ---
128
+ async function init() {
127
129
  try {
128
130
  const res = await fetch('/api/plugins/ezoic-infinite/config');
129
131
  config = await res.json();
130
- } catch (e) { console.error('[ezoic] config fetch failed'); }
131
- }
132
-
133
- function schedule() {
134
- if (scheduleTimer) clearTimeout(scheduleTimer);
135
- scheduleTimer = setTimeout(inject, 250);
136
- }
132
+
133
+ inject();
137
134
 
138
- function init() {
139
- fetchConfig().then(() => {
140
- schedule();
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
- }
135
+ // MutationObserver pour détecter le scroll infini
136
+ const observer = new MutationObserver((mutations) => {
137
+ if (mutations.some(m => m.addedNodes.length > 0)) {
138
+ if (scheduleTimer) clearTimeout(scheduleTimer);
139
+ scheduleTimer = setTimeout(inject, 300);
150
140
  }
151
141
  });
152
- mo.observe(body, { childList: true, subtree: true });
153
- });
142
+ observer.observe(document.body, { childList: true, subtree: true });
154
143
 
155
- // Compatibilité jQuery/NodeBB Events
156
- if (window.jQuery) {
157
- window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', () => {
158
- setTimeout(schedule, 500);
159
- });
160
- }
144
+ if (window.jQuery) {
145
+ window.jQuery(window).on('action:ajaxify.end action:infiniteScroll.loaded', () => {
146
+ setTimeout(inject, 500);
147
+ });
148
+ }
149
+ } catch (e) {}
161
150
  }
162
151
 
163
152
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init);
package/public/style.css CHANGED
@@ -1,67 +1,28 @@
1
- /* --- Conteneur principal de la publicité --- */
1
+ /* Conteneur de pub */
2
2
  .nodebb-ezoic-wrap {
3
- display: block !important;
4
- width: 100% !important;
5
- clear: both !important;
6
- margin: 20px 0 !important;
7
- padding: 0 !important;
8
- /* Empêche les débordements visuels lors du recyclage du DOM */
9
- overflow: hidden;
10
- /* Améliore les performances de rendu sur NodeBB 4.x */
11
- contain: layout style;
3
+ display: block !important;
4
+ width: 100% !important;
5
+ clear: both !important;
6
+ margin: 25px 0 !important;
7
+ min-height: 100px; /* Crucial pour que Ezoic détecte l'emplacement */
8
+ text-align: center;
12
9
  }
13
10
 
14
- /* --- Protection du premier emplacement (Pinned) --- */
11
+ /* Fix spécifique pour le premier sujet */
15
12
  .nodebb-ezoic-wrap[data-ezoic-pinned="true"] {
16
- /* On réserve une hauteur minimale pour éviter que le contenu
17
- ne saute violemment quand la pub charge au scroll up */
18
- min-height: 120px;
19
- border-bottom: 1px solid rgba(0,0,0,0.05);
20
- margin-top: 0 !important;
13
+ min-height: 150px;
14
+ border-bottom: 1px solid rgba(0,0,0,0.05);
15
+ margin-top: 0 !important;
21
16
  }
22
17
 
23
- /* --- Le placeholder Ezoic interne --- */
24
- [id^="ezoic-pub-ad-placeholder-"] {
25
- margin: 0 auto !important;
26
- padding: 0 !important;
27
- text-align: center;
28
- min-height: 1px;
29
- line-height: 0;
30
- }
31
-
32
- /* --- Nettoyage des éléments Ezoic internes --- */
33
- .nodebb-ezoic-wrap span.ezoic-ad,
34
- .nodebb-ezoic-wrap .ezoic-ad {
35
- display: block !important;
36
- margin: 0 auto !important;
37
- /* Force Ezoic à respecter la largeur du flux forum */
38
- max-width: 100% !important;
39
- }
40
-
41
- /* --- Gestion des blocs vides (Anti-Holes) --- */
42
- /* Si Ezoic ne renvoie rien ou si le bloc est vide,
43
- on réduit l'espace pour ne pas avoir de gros trous blancs */
18
+ /* Cache les blocs vides pour éviter les trous blancs */
44
19
  .nodebb-ezoic-wrap:empty {
45
- height: 0 !important;
46
- min-height: 0 !important;
47
- margin: 0 !important;
48
- }
49
-
50
- /* --- Adaptation spécifique pour les Messages (Sujets ouverts) --- */
51
- .ezoic-msg-first {
52
- margin-bottom: 30px !important;
53
- border-bottom: 1px solid var(--border-color, #eee);
54
- padding-bottom: 10px !important;
55
- }
56
-
57
- /* --- Support du mode sombre (NodeBB 4 Harmony) --- */
58
- [data-theme="dark"] .nodebb-ezoic-wrap[data-ezoic-pinned="true"] {
59
- border-bottom-color: rgba(255,255,255,0.1);
20
+ height: 0 !important;
21
+ min-height: 0 !important;
22
+ margin: 0 !important;
60
23
  }
61
24
 
62
- /* --- Neutralisation des marges forcées par Ezoic sur mobile --- */
63
- @media (max-width: 767px) {
64
- .nodebb-ezoic-wrap {
65
- margin: 10px 0 !important;
66
- }
25
+ /* Évite que les pubs "volent" au scroll */
26
+ .nodebb-ezoic-wrap .ezads-sticky-intradiv {
27
+ position: static !important;
67
28
  }